为何下述代码未触发SFINAE错误?与即时上下文相关的疑问
为什么没触发SFINAE?
核心原因很简单:你代码里触发的错误不在模板参数替换的「即时上下文」范围内,所以SFINAE规则不生效,直接导致编译硬错误。
具体拆解:
- SFINAE只处理「模板参数替换过程中,在即时上下文里发生的错误」。所谓即时上下文,指的是和模板参数替换直接绑定的表达式、类型推导过程——比如替换模板参数后直接导致的类型不完整、表达式非法,但这种非法必须是替换直接引发的,不能是模板定义之外的名字查找失败。
- 你写的特化模板里,
decltype(&T::empty)是直接尝试获取T的empty成员函数指针。当T是my_string时,编译器会执行名字查找寻找my_string::empty,但这个查找失败不属于即时上下文——名字查找是在模板定义的上下文里进行的,不是替换参数才触发的,而且错误根源是T本身没有这个成员,不是参数替换导致的。 - 这种情况下,编译器不会触发SFINAE跳过该特化,直接抛出编译错误。
正确的SFINAE写法
要正确检测T是否有返回bool的empty成员,得把检测逻辑放进即时上下文里,比如用std::void_t包裹成员调用的表达式:
#include <type_traits> #include <utility> struct my_string { }; template <class T, class = std::void_t<>> struct got_empty : std::false_type {}; // 用declval模拟调用,将检测逻辑放在即时上下文内 template <class T> struct got_empty<T, std::void_t< decltype(std::declval<T>().empty()), std::enable_if_t<std::is_same_v<decltype(std::declval<T>().empty()), bool>> >> : std::true_type {}; static_assert(!got_empty<my_string>::value); // 正常编译通过
这里的关键是std::declval<T>().empty()这个表达式,它只会在模板参数替换时被求值,它的非法(比如T没有empty成员)属于即时上下文错误,编译器会触发SFINAE忽略该特化,转而使用主模板。
内容的提问来源于stack exchange,提问作者minex
相关产品推荐
相关产品推荐

