I want to check if a specific member function of a class is defined and simultaneously not disabled by SFINAE. The following code works:
struct A {
int foo() { return 3; }
};
struct B {};
template <class, class = void>
struct hasFoo : std::false_type {};
template <class T>
struct hasFoo<T, std::void_t<decltype(std::declval<T>().foo())>>
: std::true_type {};
template <class T>
struct hasFoo20 : std::bool_constant < requires(T t) {
t.foo();
} > {};
static_assert(hasFoo<A>::value);
static_assert(!hasFoo<B>::value);
static_assert(hasFoo20<A>::value);
static_assert(!hasFoo20<B>::value);
but if I have a member function in A
, which itself is enabled or disabled using SFINAE, I cannot get it the following working:
template <typename T = int>
struct A {
template <typename = void, std::enable_if_t<std::is_same_v<T, int>, bool> = true>
T foo() {
return 3;
}
};
template <class, class = void>
struct hasFoo : std::false_type {};
template <class T>
struct hasFoo<T, std::void_t<decltype(std::declval<T>().foo())>>
: std::true_type {};
template <class T>
struct hasFoo20 : std::bool_constant < requires(T t) {
t.foo();
} > {};
static_assert(hasFoo<A<>>::value);
static_assert(!hasFoo<A<double>>::value);
static_assert(hasFoo20<A<>>::value);
static_assert(!hasFoo20<A<double>>::value);
Link For the second case, only the C++20 code works as I want it, but I have to make it C++17 compatible.
The SFINAE code fails with error: no type named 'type' in 'struct std::enable_if<false, bool>'
, which is expected since the enable_if_t
of foo
is evaluated.
Can someone give me a hint? Thanks!
The first step of substitution failure is not an error (SFINAE) is substitution. If a template doesn't substitute anything and causes an error, it's just a hard error. Therefore, SFINAE must be dependent on the template parameters it appears in. Even though T
looks like a template parameter of foo
, it's not, it's from the enclosing class.
One solution is
template <typename U = T, std::enable_if_t<std::is_same_v<U, int>, bool> = false>
T foo() {
return 3;
}