使用SFINAE实现类型检查报错,test函数参数末尾*号的作用是什么?
两种写法效果不同的核心原因
两种写法的差异本质是重载决议阶段的参数匹配规则不同,和SFINAE的替换逻辑本身没有直接关联:
1. 指针参数版本正常工作的逻辑
当test参数为reallyHas<string (C::*)(), &C::serialize>*指针类型时:
- 你调用
test<T>(0)时传入的实参0是空指针常量,可以合法隐式转换为任意类型的空指针 - 如果
T存在对应签名的serialize成员函数,reallyHas的模板参数就能成功实例化,这个模板重载的优先级远高于接收任意参数的test(...)兜底重载,会被优先选中,sizeof计算得到yes类型的大小,最终value为true - 如果
T不存在对应签名的函数,模板替换失败触发SFINAE,这个重载被移出候选集,最终匹配到兜底重载,value为false
2. 值参数版本失效的逻辑
当去掉*改为值类型reallyHas<string (C::*)(), &C::serialize>时:
- 你传入的实参是
int类型的0,而reallyHas是没有自定义构造函数的空结构体,不存在从int到reallyHas实例的合法隐式转换路径 - 哪怕
T存在对应签名的serialize函数、reallyHas模板可以正常实例化,这个重载本身也会因为参数类型不匹配被直接排除在候选集之外,最终只会匹配到test(...)兜底重载,所以不管有没有对应函数,value永远是false,检测逻辑失效。
值参数版本的修正方案
如果想要用值类型参数也能正常工作,可以给test函数加上默认参数,调用时不需要传实参即可:
template<typename T> struct has_serialize { typedef char yes[1]; typedef char no[2]; template<typename U, U u> struct reallyHas{}; template <typename C> static yes& test(reallyHas<string (C::*)(), &C::serialize> = {}){} // 针对const方法的重载 template<typename C> static yes& test(reallyHas<string (C::*)() const, &C::serialize> = {}){} template<typename> static no& test(...){} enum { value = (sizeof(test<T>()) == sizeof(yes)) }; };
内容的提问来源于stack exchange,提问作者Bad_Panda
相关产品推荐
相关产品推荐

