Home > Mobile >  Template constraints on operator overloading does not work as expected
Template constraints on operator overloading does not work as expected

Time:12-08

#include <tuple>
#include <utility>

template<typename T>
struct is_tuple_like : std::false_type {};

template<typename... Ts>
struct is_tuple_like<std::tuple<Ts...>> : std::true_type {};

template<typename T, typename U>
struct is_tuple_like<std::pair<T, U>> : std::true_type {};

template<typename T>
concept tuple_like = is_tuple_like<T>::value;

template<tuple_like L, tuple_like R, int N = std::tuple_size_v<L>>
auto operator*(const L &lhs, const R &rhs) { return 0; }

enum { Enum };

int main()
{
    Enum * Enum; // causes compilation error
    return 0;
}

You can run the code here: http://coliru.stacked-crooked.com/a/f65e333060f40e60

I have defined a concept so-called tuple_like and overloaded operator*() using the concept.

Then, If I multiply enums, my overloaded operator*() for tuple_like is picked up and the compiler complains missing std::tuple_size for enum.

What did I do wrong here and how can I fix it without overload for each class templates - std::tuple and std::pair?

FYI, even if it's unusual, I cannot remove the part of multiplying enums because it's not my code.

CodePudding user response:

Turn tuple_size_v into tuple_size::value to enable SFINAE

template<tuple_like L, tuple_like R, int N = std::tuple_size<L>::value>
auto operator*(const L &lhs, const R &rhs) { return 0; }

However, I don't see any value in declaring an extra template parameter N here. The type that satisfies tuple_like already guarantees that tuple_size_v is a valid value.

It would be more appropriate to move N into the function body

template<tuple_like L, tuple_like R>
auto operator*(const L &lhs, const R &rhs) { 
  constexpr auto N = std::tuple_size_v<L>; // guaranteed to work
  // uses N below
}

CodePudding user response:

Looks like gcc can't handle SFINAE for default values of template arguments. This workaround fixes it:

template<tuple_like L, tuple_like R, int N>
auto operator*(const L &lhs, const R &rhs) { return 0; }

template<tuple_like L, tuple_like R>
auto operator*(const L &lhs, const R &rhs) { return operator*<L, R, std::tuple_size_v<L>>(lhs, rhs); }

  • Related