Reputation:
struct BLA
{
};
template<typename T>
class DUMMY
{
public:
DUMMY() = default;
template<typename U = T, typename = void>
void someFunction()
{
std::cout << std::is_same<U, BLA>::value << "\n";
std::cout << "someFunction() - DEFAULT\n";
}
template<typename U = T, typename std::enable_if<std::is_same<U, BLA>::value, void>::type>
void someFunction()
{
std::cout << "someFunction()\n";
}
};
int main()
{
DUMMY<BLA> dummy;
dummy.someFunction();
}
Why is this SFINAE code calling someFunction() that displays "someFunction() - DEFAULT"? It should call the other one. It is clear that std::is_same::value is true.
Upvotes: 1
Views: 201
Reputation: 1282
I am writing as new answer because it doesn't fit to comment. For addition to @Jarod42.
It seems you assumed that
template<typename U = T, typename std::enable_if<std::is_same<U, BLA>::value, void>::type>
substitutes to
template<typename U = T, typename = void>
but it doesn't. It substitutes to
template<typename U = T, void>
.
So, you should declare it as
template<typename U = T, typename = typename std::enable_if<std::is_same<U, BLA>::value, void>::type>
.
Because the typename
you used is for specifying the type is dependant, not for the template parameter declaration. But either won't work as you expected anyway. One silently removes the ill-formed, other one results in multiple declaration of the same function.
After your comment, i try to explain more detailed.
template<typename U = T, typename std::enable_if<std::is_same<U, BLA>::value, void>::type>
void someFunction()
{
std::cout << "someFunction()\n";
}
If T == BLA
, U
becomes BLA
and it makes std::is_same< U , BLA>::value
true
. So the result looks like this
template<typename U = BLA, void>
If T == NotBlaType
, U
becomes NotBlaType
and it makes std::is_same<U,BLA>::value
false
. The result is substition failure, std::enable_if<false,void>
has no type
.
But in both case, the function is not declared. Because void
cannot be allowed as non-type template parameter.
But if we change void
to int
, then it is allowed. That's why @Jarod42 suggests int
.
template<void>
is not legal.
But template<int = 2>
is legal.
After making the declaration is valid, you should toggle declarations of functions conditionally (because both functions have same signature so causes to multiple declaration). That's why both functions in the @Jarod42's answer have std::enable_if
which evalutes negate of each other.
Upvotes: 0
Reputation: 218278
template<typename U = T, typename std::enable_if<std::is_same<U, BLA>::value, void>::type>
would result (with correct substitution) in template<typename U = T, void>
which is invalid.
You might change to
template<typename U = T, typename std::enable_if<std::is_same<U, BLA>::value, int>::type = 0>
but then, both function would be viable, and so ambiguous.
So, you might finally do
template<typename U = T, typename std::enable_if<!std::is_same<U, BLA>::value, int>::type = 0>
void someFunction()
{
std::cout << std::is_same<U, BLA>::value << "\n";
std::cout << "someFunction() - DEFAULT\n";
}
template<typename U = T, typename std::enable_if<std::is_same<U, BLA>::value, int>::type = 0>
void someFunction()
{
std::cout << "someFunction()\n";
}
In C++17, simpler to do
void someFunction()
{
if constexpr (std::is_same<U, BLA>::value) {
std::cout << "someFunction()\n";
} else {
std::cout << std::is_same<U, BLA>::value << "\n";
std::cout << "someFunction() - DEFAULT\n";
}
}
Upvotes: 2