C++中的表达式SFINAE和硬错误
我很困惑为什么这些函数中的一个会出现硬错误,而另一个不会:
#include <utility>
template <typename ...Args>
auto ffExists1()
-> decltype(ff(std::declval<Args>()...)); // Attempts to see
// whether you can call ff (which is not defined or declared)
// with arguments of type Args... This works fine.
template <typename ...Args>
auto ffExists2() -> decltype(&ff); // Gives me a hard error : ff not defined
这是为什么?另外,我该如何ffExists2工作?使用指向函数的指针可以让我确定ffwhile的确切签名,ffExists1只知道我是否可以ff使用类型的参数进行调用Args...。
编辑:这是一个ffExists2依赖于模板的版本,导致相同的结果:
#include <utility>
template <typename ...Args>
auto ffExists() -> decltype(ff(std::declval<Args>()...)); //Fine
template <typename ... Args>
void SomeHelperFunc(auto (*) (Args...));
template <typename ...Args>
auto ffExists2() -> decltype(SomeHelperFunc<Args...>(&ff)); // Hard Error
回答
在ffExists2,ff不依赖于模板参数,因此它的查找(即通过提供的名称查找函数)是在两阶段查找的第一阶段完成的,即当编译器第一次看到模板时,而不是在模板参数被替换到其中。
正因为如此,即使ff是在这个模板之后定义的,也没关系,因为到那时第一阶段已经完成了。
另一方面,在 中ffExists1, 的查找ff取决于模板参数(因为ADL是此查找的一部分,而 ADL 需要知道参数的类型,即Args...),因此对其的查找推迟到第二阶段,即模板的实例化点,
此时 ADL 检查从模板定义上下文以及模板实例化上下文可见的函数声明,而非 ADL 查找仅检查从模板定义上下文可见的函数声明(换句话说,添加一个新函数模板定义后的声明不会使其可见,除非通过 ADL)。
— (c) cppreference
无法&ff依赖模板参数,也不可能对不依赖模板参数的事物进行 SFINAE 检查。任何对它的尝试都会受到以下因素的阻碍:
[temp.res.general]/6.1程序格式错误,无需诊断,如果:
— 无法为模板生成有效的专业化...
您可能会认为 C++20requires会有所帮助,因为您可以在没有任何模板的情况下使用它,但是:
[expr.prim.req.general]/5...
[注 1:如果一个 requires 表达式在其需求中包含无效类型或表达式,并且它没有出现在模板化实体的声明中,那么程序就是格式错误的。— 尾注]
如果将模板参数替换为需求总是会导致替换失败,则程序格式错误;无需诊断。