I'm currently studying enable_if
and I have this code:
//template<typename T, typename = int/double/float/...> //not working properly
template<typename T, typename = void> //works fine
struct test{
void func(){
cout << "default" << endl;
}
};
template<typename T>
struct test<T, typename std::enable_if<(sizeof(T) <= 1)>::type>{
void func(){
cout << "called" << endl;
}
};
int main() {
test<char> objs1;
objs1.func(); //called
test<int> objs2;
objs2.func(); //default
}
I don't know the reason why I have to set the second parameter's default value as void
. If I set it to other values like int
or float
or double
, both objs1.func();
and objs2.func();
will print default. What is the reason?
CodePudding user response:
So, std::enable_if<...>::type
is, in fact, a type. Because you didn't specify what the type should be, you just specified the condition for which it exists at all, the default is void
.
Let's look at your second version of the template. If sizeof(T) <= 1
, you provide a template specialization for test<T, void>
. Otherwise, the substitution fails and you provide nothing.
Now let's consider what happens when you just write test<char> objs1;
. In your original version, because the default value for the unnamed second template parameter was void
, this means objs1
is actually of type test<char, void>
. And we actually have a specialization for test<char, void>
, because sizeof(char) <= 1
is true
.
However, if you change the default value of the unnamed second template parameter, we get a very different situation. Say you make the default value int
instead of void
. Then test<char> objs1;
is actually declaring an object of type test<char, int>
. We have a specialization defined for test<char, void>
... But we aren't trying to create a test<char, void>
, we're trying to create the separate type test<char, int>
. So the fact that the condition of the enable_if
is true
is neither here nor there and we get the default definition of test
.
CodePudding user response:
The technique that is being used is SFINAE implemented via partial template specialization. In order to have multiple different types of test
s depending on the characteristics of T
we need to have the SFINAE expression in the template parameter list. Since class cannot be overloaded you build an "overload set" by creating a main default template and then partial specializations for all of the different cases. To do that the main template needs to have two parameters, T
and the type that enable_if
will resolve to. We default that second parameter to void
so that it does not need to be specified by the caller to get the main template.