SFINAE成员函数检查类型特征使用成员函数指针时Clang编译失败
问题根因
你写的版本(2)的硬错误来自非类类型构造成员指针类型属于非法操作,且该操作发生在SFINAE上下文之外,无法被忽略:
- 当
T = double时,has_print_method<double>实例化过程中,直接在类作用域内用到了void (T::*)() const类型,double是非类类型,不存在成员指针,直接触发硬错误,还没进入内部helper的SFINAE检查逻辑。 - 版本(1)不会触发该问题的原因是:
&T::print的替换发生在偏特化的模板参数列表中,属于SFINAE允许的替换失败场景,非类类型T匹配失败后直接落到主模板的std::false_type,不会产生硬错误。 - GCC能编译通过属于不符合C++标准的实现偏差,旧版GCC对非类类型的成员指针类型构造检查宽松,属于编译器bug,高版本GCC也会复现clang的报错。
版本(2)修复方案
你可以把签名检查放到SFINAE替换上下文内,用标准的std::void_t写法实现,同时兼容非类类型的检查:
#include <type_traits> template <typename T, typename = void> struct has_print_method : std::false_type {}; // 仅当T存在print成员、且print的签名是void() const时匹配该特化 template <typename T> struct has_print_method<T, std::void_t<decltype(&T::print)>> : std::is_same<decltype(&T::print), void (T::*)() const> {}; template <typename T> const bool has_print_method_v = has_print_method<T>::value;
如果需要兼容print存在重载的场景,可以改用调用表达式检查,避免取成员地址因为重载失败:
template <typename T> struct has_print_method<T, std::void_t<decltype(std::declval<const T&>().print())>> : std::is_same<decltype(std::declval<const T&>().print()), void> {};
内容的提问来源于stack exchange,提问作者Ronaldinho
相关产品推荐
相关产品推荐

