3
#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.

slyx
  • 2,063
  • 1
  • 19
  • 28
  • What's `int N` for? – HolyBlackCat Dec 07 '22 at 17:15
  • 1
    Related: https://stackoverflow.com/a/13730889/27678 https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1554 and https://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1844 Basically the question is how template aliases participate in SFINAE – AndyG Dec 07 '22 at 17:19

2 Answers2

4

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
}
康桓瑋
  • 33,481
  • 5
  • 40
  • 90
0

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); }

lorro
  • 10,687
  • 23
  • 36