I guess any use of SFINAE could be considered a hack, but here I tried for a long time and the best I could manage was to use a default void* argument in one of the overloads:
struct Dog
{
Dog() {}
void makeNull() {}
};
// If no .makeNull() function this is eliminated
template <typename T>
constexpr auto HasMakeNullFunction() -> decltype(std::declval<T>().makeNull(), bool())
{
return true;
}
// And this one is called. But I could only manage to do it with a default void* p = nullptr
template <typename T>
constexpr bool HasMakeNullFunction(void* p = nullptr)
{
return false;
}
int main()
{
constexpr bool b = HasMakeNullFunction<Dog>(); // True
constexpr bool b2 = HasMakeNullFunction<int>(); // False
}
What's the way you're supposed to do it? This does work, but the typical way to use SFINAE is with a specialized version that gets called when the substitution fails, right? Also, I don't like the use of the default void* as I could see a potential for a misuse and an implicit conversion to void*.