什么是适用于SFINAE的C ++ 11标准中提到的“直接上下文”?

C ++ 11标准第14.8.2 / 8段规定了replace失败应该或不应该导致“硬”编译错误(从而导致编译失败)或“软”错误的条件导致编译器从重载parsing的候选集合中丢弃一个模板(不会使编译失败,并启用众所周知的SFINAE成语):

如果replace导致无效的types或expression式,则键入演绎失败。 无效的types或expression式是使用replace参数编写的格式不正确的types或expression式。 [注意:访问检查是替代过程的一部分。 -end note] 只有函数types及其模板参数types的上下文中的无效types和expression式才会导致扣除失败 。 […]

在“整个C ++ 11标准”中,“ 直接上下文 ”这个词只出现了8次,每次它们跟随(或作为其一部分出现)下列(非规范性)文本的实例:

[注意: 替代types和expression式的评估可能导致副作用,例如类模板特化和/或函数模板特化的实例化,隐式定义函数的生成等。这些副作用不在“即时上下文“,并可能导致程序不合格。 – 注意]

这个注释给出了直接上下文意味着什么(不是非常慷慨的)暗示,但是至less对于我来说,这往往不足以决定replace是否应该导致“硬”编译错误。

题:

你能提供一个解释,一个决策程序,和/或一些具体的例子来帮助搞清楚在哪些情况下,replace错误在函数types及其模板参数types的“ 直接上下文 ”中是否发生?

如果考虑确定模板参数replace结果所需的所有模板和隐式定义的函数,并想象它们是在replace开始之前首先生成的,则在第一步中发生的任何错误都不在紧邻的上下文中,并导致严重的错误。

如果所有这些实例化和隐式定义(可能包括将函数定义为已删除的)都可以没有错误地完成,那么在replace期间发生的任何进一步的“错误”(即在函数模板中引用实例化的模板和隐式定义的函数签名)不是错误,但会导致扣除失败。

所以给定一个这样的函数模板:

template<typename T> void func(typename T::type* arg); 

和一个“回退”,如果扣除其他function失败将使用:

 template<typename> void func(...); 

和这样的类模板:

 template<typename T> struct A { typedef T* type; }; 

func<A<int&>>(nullptr)代替T ,为了检查T::type存在,它必须实例化A<int&> 。 如果我们想象在调用func<A<int&>(nullptr)之前放置一个明确的实例:

 template class A<int&>; 

那么就会失败,因为它试图创buildtypesint&*并且不允许指向引用的指针。 我们没有到达检查replace是否成功的地步,因为实例化A<int&>存在一个硬性错误。

现在让我们假设有一个明确的专门化的A

 template<> struct A<char> { }; 

func<A<char>>(nullptr) A<char> func<A<char>>(nullptr)的调用需要A<char>的实例化,所以想象在调用之前程序中的某处显式实例化:

 template class A<char>; 

这个实例是好的,从这个没有错误,所以我们继续进行参数replace。 A<char>的实例化工作,但是A<char>::type不存在,但是这是可以的,因为它只在func的声明中被引用,所以只会导致参数推导失败,而后退...函数被调用。

在其他情况下,replace可能导致特殊成员函数被隐式定义,可能被删除,这可能触发其他实例化或隐式定义。 如果在“生成实例化和隐式定义”阶段发生错误,那么它们就是错误,但是如果成功了,但是在replace过程中,函数模板签名中的expression式就变成无效的,例如因为它使用了不存在的成员或一些被隐式定义为删除的东西,这不是一个错误,只是一个扣除失败。

所以我使用的思维模型是,替代需要先做一个“准备”步骤来产生types和成员,这可能会导致严重的错误,但是一旦我们完成了所有必要的生成,任何进一步的无效使用都不是错误。 当然,这只是把问题从“ 即时语境是什么意思? 到“在replace之前需要生成哪些types和成员?” 所以它可能会或可能不会帮助你!

直接的上下文基本上是你在模板声明中看到的。 除此之外的一切都是一个严重的错误。 硬性错误的例子:

 #include <type_traits> template<class T> struct trait{ using type = typename T::type; }; template<class T, class U = typename trait<T>::type> void f(int); void f(...); template<class T, class U = typename T::type> void g(int); void g(...); template<class> struct dependent_false : std::false_type{}; template<class T> struct X{ static_assert(dependent_false<T>(), "..."); using type = void; }; int main(){ f<int>(0); g<X<int>>(0); } 

Live版本。