I know that a C compiler picks a template specialization in preference to the primary template:
template<class T, class Enable = void>
class A {}; // primary template
template<class T>
class A<T, std::enable_if_t<std::is_floating_point_v<T>, void>> {
}; // specialization for floating point types
However, I don't understand why the selection fails when a type other than void
is used as argument to enable_if
:
template<class T>
class A<T, std::enable_if_t<std::is_floating_point_v<T>, int>> {
}; // specialization for floating point types
The compiler would surely "see" class A<T, int>
.
So it certainly can't be SFINAE, nevertheless the primary template is preferred to the specialization.
This question arises from a context where a custom type detection machinery could be used instead of enable_if
, e.g. an SFINAE friendly type extractor like common_type
.
CodePudding user response:
When you have
A<some_floating_point_type> some_name;
The template parameters are some_floating_point_type
and the implicit void
. When the compiler instantiates
template<class T>
class A<T, std::enable_if_t<std::is_floating_point_v<T>, int>> {
}; // specialization for floating point types
it would get A<some_floating_point_type, int>
, which does not match the A<some_floating_point_type, void>
type that some_name
has. Because of that, the specialization is ignored and you get the primary template. You can verify this by trying to create a A<some_floating_point_type, int>
and you'll get that the specialization was picked.
I find it helpful to think of a specialization as an alternate recipe. First the template arguments are deduced, and then if they match any of the specializations, then we switch to using that alternate recipe. If not, then the original recipe is used.
CodePudding user response:
Specializations are irrelevant until the compiler knows which types it is going to use for the primary template.
When you write A<double>
, then the compiler looks only at the primary template and sees that you actually mean A<double,void>
.
And only then it is looking for specializations. Now, when your specialization is for A<double,int>
, then it is not suitable because you asked for A<double,void>
.
CodePudding user response:
In simple terms, this
template<class T, class Enable = void>
class A {};
says "A is a template, it has two arguments, the second has a default that is void
". Thats the primary template. When you explicitly provide only one parameter then the second argument is void
.
Now the specialization:
template<class T>
class A<T, std::enable_if_t<std::is_floating_point_v<T>, int>> {
};
This doesn't change the above. A
is still a template with 2 arguments and when the second one is not specified then it is void
. In simple terms it either means "substituting T
in std::enable_if_t<std::is_floating_point_v<T>
is a failure, because the type alias does not exist" when the condition is false
, SFINAE kicks in and the specialization is ignored. When the condition is true
: "Whenever A is instantiated with arguments T (some type) and int
then use this definition".
When you instantiate A<int>
then the second template argument is void
. The specialization does not change that. That is A<int>
is actually A<int,void>
. It does not match the specialiazation.