I have a program that is as follows. There is a base template struct X
and a partial specialisation with SFINAE.
template <typename T, typename U = void>
struct X{
X() {
std::cout << "in 1" << std::endl;
};
};
template <typename T>
struct X< T, std::enable_if_t<std::is_integral_v<T>> > {
X() {
std::cout << "in 2" << std::endl;
};
};
int main() {
X<int> x;
}
When running the program in 2
is printed.
Why is it that the second specialization is chosen over the first since both of them effectively declare a struct X<int, void>
. What makes std::enable_if_t<std::is_integral_v<T>>
more specialized than a default template type argument as shown in the base template?
Why does the default type argument of the base template have to be the same as the type defined by the partial specialization for the partial specialization to be called and in 2
to be printed.
Why does changing to std::enable_if_t<std::is_integral_v<T>, bool>
cause the base template in 1
to be called?
The answers to your questions lie in Template Partial Ordering. This is the mechanism the compiler uses to determine which template is the best fit (be it a function template overload, or in your case, a class template specialization).
In brief, your generic template implementation has 2 parameters T
and U
, whereas your SFINAE specialization have only the T
parameter, while the second is deduced from T
. It is therefore more specialized than the general case and in the end, when you refer to X<int, void>
, the specialization is chosen.
Now question 2. Suppose we replace the enable_if
parameter with bool
instead of void
. Now our specialization will be X<int, bool>
instead of X<int, void>
, so when you refer to X<int>
, i.e. X<int, void>
, it doesn't match the specialization anymore because those are 2 different types.