为何函数返回类型未被SFINAE直接上下文捕获?C++编译报错
问题:为何SFINAE未捕获构造失败的硬错误?
我觉得自己漏了些显而易见的点。下面是可运行的代码:
#include <type_traits> template <typename T, typename ...P> struct construct { decltype(T(std::declval<P>()...)) foo(); }; template <typename T> concept int_constructible = requires { construct<T, int>{}.foo(); }; struct A {}; static_assert(!int_constructible<A>);
我预期这段代码能编译通过,但GCC、Clang和MSVC均触发了未被SFINAE捕获的硬错误:
<source>:6:14: error: no matching conversion for functional-style cast from 'int' to 'A' decltype(T(std::declval<P>()...)) foo(); ^~~~~~~~~~~~~~~~~~~
我遗漏了什么?foo()的返回类型在我看来属于直接上下文的一部分。
解答
核心问题出在**construct<T, int>类模板的实例化阶段**,而非foo()函数的返回类型推导。
当你在requires表达式中写construct<T, int>{}.foo();时,编译器必须先实例化construct<T, int>这个类模板。类模板实例化时,会处理类内部的所有成员声明(包括成员函数的签名)——此时decltype(T(std::declval<P>()...))会被求值,而这个求值过程属于类模板实例化的上下文,并不在SFINAE的直接覆盖范围内。
SFINAE仅作用于函数模板重载决议或概念约束检查的直接上下文错误,类模板实例化产生的错误属于硬错误,无法被SFINAE屏蔽。
要修复这个问题,需要把返回类型推导移到函数模板中,让SFINAE能够生效:
#include <type_traits> template <typename T, typename... P> struct construct { template <typename... Args> auto foo(Args&&...) -> decltype(T(std::declval<P>()...)); }; template <typename T> concept int_constructible = requires { construct<T, int>{}.foo(); }; struct A {}; static_assert(!int_constructible<A>);
或者更简洁的方案:直接在requires表达式中检查构造行为,无需额外类模板:
#include <type_traits> template <typename T> concept int_constructible = requires(int i) { T(i); }; struct A {}; static_assert(!int_constructible<A>);
修改后,编译器会在概念约束检查过程中直接尝试T(i)的构造操作,此时的错误属于直接上下文,会被SFINAE正确处理,不会触发硬错误。
内容的提问来源于stack exchange,提问作者HolyBlackCat
相关产品推荐
相关产品推荐

