简体   繁体   中英

Why when use enable_if in class template have to set the second parameter's default type as void?

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?

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 .

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM