为何在SFINAE中必须使用void_t?
关于void_t在SFINAE中必要性的解释
你的代码存在的核心问题
你的思路忽略了一个关键:特化版本的第二个模板参数类型必须和主模板的默认参数类型(void)完全匹配,才能触发特化。
假设T::member的类型不是void(比如是int、std::string或者其他类型),那么decltype(T::member)的结果就是该成员的实际类型。此时特化版本的第二个参数是该成员类型,而你使用has_member<T>时,主模板的第二个参数默认是void,模板匹配会优先找最贴合的特化——但这里特化的第二个参数和void不匹配,所以编译器会直接实例化主模板,返回false_type,这和你预期的结果完全相反。
举个实际例子:
struct Test { int member; }; // 使用你的代码,has_member<Test>::value会是false,而非预期的true
为什么必须用void_t?
void_t的核心作用就是把任意合法的表达式统一转换成void类型。不管T::member是什么类型,std::void_t<decltype(T::member)>的结果都是void,这样特化版本的第二个参数就和主模板的默认参数void完全匹配:当T::member存在时,编译器会优先选择这个特化版本,返回true_type;当T::member不存在时,SFINAE会丢弃这个特化,转而实例化主模板。
修正后的正确代码:
template< class , class = void > struct has_member : std::false_type { }; template< class T > struct has_member< T , std::void_t<decltype( T::member )> > : std::true_type { };
关于cppreference的查阅章节
你可以查阅cppreference中**std::void_t**的专属章节,里面详细说明了它的用途和SFINAE相关的应用场景;另外,SFINAE的章节也会讲解模板特化匹配的规则,帮助你理解模板参数匹配的优先级和条件。
内容的提问来源于stack exchange,提问作者Dean DeRosa
相关产品推荐
相关产品推荐

