为何类模板使用enable_if时需将第二个参数默认设为void?
为什么std::enable_if配合模板特化时默认参数必须是void?
要搞懂这个问题,得从模板特化的匹配规则和std::enable_if的工作机制两个角度拆解:
先明确std::enable_if的默认行为
std::enable_if的简化定义如下:
template<bool B, typename T = void> struct enable_if {}; template<typename T> struct enable_if<true, T> { using type = T; };
它的第二个模板参数默认是void,只有当第一个条件B为真时,结构体才会包含type成员,且type的类型就是你指定的T(默认是void)。
主模板默认参数为void时的工作逻辑
你的正常代码中,主模板定义为:
template<typename T, typename = void> struct test{...};
- 实例化
test<char>时,等价于test<char, void>(第二个参数使用默认值void)。此时特化模板的条件sizeof(char)<=1为真,std::enable_if<...>::type就是默认的void,特化模板test<T, void>和实例化类型完全匹配,编译器优先调用特化版的func,输出"called"。 - 实例化
test<int>时,sizeof(int)<=1为假,std::enable_if<...>没有type成员,触发**SFINAE(替换失败不是错误)**规则,这个特化模板被编译器直接忽略,只能使用主模板,输出"default"。
换成int/float等类型时失效的原因
如果把主模板的默认参数改成int:
template<typename T, typename = int> struct test{...};
- 实例化
test<char>时,等价于test<char, int>。但特化模板里的std::enable_if<...>::type还是默认的void,所以特化模板是test<T, void>,和test<char, int>的模板参数完全不匹配,编译器只能调用主模板,输出"default"。 - 同理,
test<int>也只会匹配主模板,最终两个实例都输出default。
让默认参数是int时生效的方法
如果非要用int当主模板的默认参数,你需要显式指定std::enable_if的第二个模板参数为int,让特化模板的第二个参数和主模板默认值一致:
template<typename T, typename = int> struct test{ void func(){cout << "default" << endl;} }; template<typename T> struct test<T, typename std::enable_if<(sizeof(T) <= 1), int>::type>{ void func(){cout << "called" << endl;} };
这时候实例化test<char>就会匹配特化模板,输出"called"了。
总结
核心逻辑是:模板特化要求所有模板参数完全匹配,而std::enable_if默认导出的type是void,所以主模板用void当默认参数时,特化模板的参数才能和实例化类型匹配。换成其他类型的话,要么显式指定enable_if的第二个参数,要么特化模板就无法匹配,只能使用主模板。
内容的提问来源于stack exchange,提问作者Kevin eyeson
相关产品推荐
相关产品推荐

