Red*_*Fog 1 c++ sfinae language-lawyer return-type-deduction c++17
有一些函子具有返回类型推导,包括lambda 表达式。
constexpr auto a = [](auto&& x){ return x + 1; };
struct X{
template<typename T>
auto operator()(T&& x){
return x + 1;
}
};
Run Code Online (Sandbox Code Playgroud)
然后,我有 2 个函数来检查参数是否可以应用于这些函子,即SFINAEstd::is_invocable_v和SFINAE。
template<typename F, typename T, std::enable_if_t<std::is_invocable_v<F, T>, int> = 0>
void foo(T a){
std::cout << "yes" << std::endl;
}
template<typename F, typename T, std::enable_if_t<!std::is_invocable_v<F, T>, int> = 0>
void foo(T a){
std::cout << "no" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
最后,我使用foo<X>(0)or foo<decltype(a)>(0),它运行良好并显示“是”,因为检查已通过。但是当我使用foo<X>((void*)0)or时foo<decltype(a)>((void*)0),我收到编译错误而不是“否”。
替换发生在
1.函数类型中使用的所有类型(包括返回类型和所有参数的类型)
...
看起来,这些函子将接受任何类型的参数,然后如果x + 1格式错误则抛出错误。但是, 的返回类型operator()是由 推导出来的x + 1,这意味着它取决于参数的类型T。当 被std::is_invocable_v实例化时,T被 替换void*,然后 的签名 operator()具有无效的返回类型。这是替代失败吗?
为了澄清这个问题,我定义了这两个函子:
struct Y{
template<typename T>
decltype(auto) operator()(T&& x){
return x + 1;
}
};
struct Z{
template<typename T>
auto operator()(T&& x)->decltype(x + 1){
return x + 1;
}
};
Run Code Online (Sandbox Code Playgroud)
如果返回类型为
decltype(auto),则返回类型与将 return 语句中使用的表达式包装在decltype.
但为什么foo<Z>((void*)0)说“不”却foo<Y>((void*)0)导致错误呢?
推导的返回类型不是 SFINAE 友好的,因为查询利用返回类型推导的模板化可调用对象(函数模板/泛型 lambda/带有 templated 的函子operator()(...))的返回类型需要实例化可调用对象的特定专业化,如定义为了推断返回类型,需要专业化:
\n\n[dcl.spec.auto]/11在声明类型中带有占位符的函数模板的返回类型推导在实例化定义时发生,即使函数体包含带有非类型相关操作数的 return 语句。[\xe2\x80\x89注意:因此,任何使用函数模板的\n特化都会导致隐式\n实例化。此实例化产生的任何错误都不是函数类型的直接上下文,并且可能导致程序格式错误([temp.deduct])。\xe2\x80\x89\xe2\x80\x94\xe2\x80\x89尾注\xe2\x80\x89] [\xe2\x80\x89示例:
\nRun Code Online (Sandbox Code Playgroud)\ntemplate <class T> auto f(T t) { return t; } // return type deduced at instantiation time\ntypedef decltype(f(1)) fint_t; // instantiates f<int> to deduce return type\ntemplate<class T> auto f(T* t) { return *t; }\nvoid g() { int (*p)(int*) = &f; } // instantiates both fs to determine return types,\n // chooses second\n\xe2\x80\x89\xe2\x80\x94\xe2\x80\x89结束示例\xe2\x80\x89]
\n
由于 的实现std::is_invocable,它应用于decltype调用可调用专业化的(未评估的)表达式(以查找返回类型),因此会触发专业化的返回类型推导,这需要实例化专业化,从而导致,在这种情况下,正如上面(非规范性)注释中所强调的,程序格式不正确。