为何给enable_if模板添加* = nullptr可避免函数重载冲突?
问题:为何给
enable_if_t添加* = nullptr能解决模板重载冲突? 先看原代码:
#include <iostream> #include <type_traits> #include <string> struct A{ int a; }; struct B{ std::string b; }; template<typename T> auto has_member_a(...) -> std::false_type; template<typename T> auto has_member_a(int v) -> decltype(std::declval<T>().a, std::true_type{}); template<typename T> auto has_member_b(...) -> std::false_type; template<typename T> auto has_member_b(int v) -> decltype(std::declval<T>().b, std::true_type{}); template <typename T, std::enable_if_t<decltype(has_member_a<T>(3))::value>> void customPrint() { std::cout<<"Has member a\n"; } template <typename T, std::enable_if_t<decltype(has_member_b<T>(3))::value>> void customPrint() { std::cout<<"Has member b\n"; } int main() { A a{5}; B b{"Ciao"}; customPrint<A>(); customPrint<B>(); return 0; }
这段代码会报重定义错误,但把两个customPrint的模板声明改成template <typename T, std::enable_if_t<decltype(has_member_a<T>(3))::value>* = nullptr>后就能正常运行,原因如下:
核心原因拆解
原写法的本质问题
原代码里的std::enable_if_t<条件>,当条件为true时,对应的类型是void——但C++标准明确禁止void类型的非类型模板参数。虽然有些编译器会宽松接受这种写法,但此时两个customPrint模板的参数列表在编译器眼里完全一致(都是typename T, void),所以会被判定为重复定义。修改后写法的作用
改成std::enable_if_t<条件>* = nullptr后,第二个模板参数变成了void*类型(条件为true时),这是合法的非类型模板参数,还带了默认值nullptr。此时关键的变化是SFINAE(替换失败不是错误)机制能正常工作:- 当实例化
customPrint<A>时,第一个模板的条件成立,第二个参数是有效的void*,模板正常参与匹配;第二个模板的条件不成立,std::enable_if_t<false>会触发替换失败,这个模板直接被排除。 - 当实例化
customPrint<B>时,情况完全相反,只有第二个模板会被保留。
同时,编译器会把这两个模板视为不同的模板(因为它们的enable_if依赖条件不同),不会再报重定义错误。
- 当实例化
内容的提问来源于stack exchange,提问作者roschach
相关产品推荐
相关产品推荐

