Home > Enterprise >  Why when use enable_if in class tempalte have to set the second parameter's default type as voi
Why when use enable_if in class tempalte have to set the second parameter's default type as voi

Time:01-18

I'm currently studying enable_if. But 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 tests 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.

  • Related