And*_*owl 37 c++ templates sfinae language-lawyer c++11
14.8.2段的C++ 11标准的/ 8指定在其下的取代失败作出或不会导致"硬"编译错误的条件(从而引起编译失败),或者在"软"差错这将只是导致编译器从一组候选中丢弃模板以进行重载解析(不使编译失败并启用众所周知的SFINAE习语):
如果替换导致无效的类型或表达式,则类型推导失败.如果使用替换参数写入,则无效的类型或表达式将是格式错误的.[注意:访问检查是作为替换过程的一部分完成的.-end note] 只有函数类型的直接上下文中的无效类型和表达式及其模板参数类型才会导致演绎失败.[...]
单词" 直接上下文 "在整个C++ 11标准中仅出现8次,并且每次出现以下(或作为其一部分)以下(非规范)文本的实例时:
[注:在取代的类型和表达式的计算可导致副作用,例如类模板特和/或功能模板特的实例化,隐含定义的函数的生成等.这样的副作用是不是在"即时上下文"并且可能导致程序格式不正确. - 尾注]
该说明给出了什么叫一个(不是非常慷慨)暗示直接背景,但至少对我来说这往往是不足以决定一个替代是否是或不应该造成"硬"的编译错误.
题:
您能否提供一个解释,一个决策程序和/或一些具体的例子来帮助弄清楚在什么情况下替换错误在函数类型及其模板参数类型的" 直接上下文 "中发生和不发生?
Jon*_*ely 33
如果考虑确定模板参数替换结果所需的所有模板和隐式定义函数,并想象它们是在替换开始之前首先生成的,那么在第一步中发生的任何错误都不在直接上下文中,并导致硬错误.
如果所有这些实例化和隐式定义(可能包括将函数定义为已删除)都可以无错误地完成,那么在替换期间发生的任何其他"错误"(即在引用实例化模板和函数模板中的隐式定义函数时)签名)不是错误,但会导致扣减失败.
所以给定一个这样的函数模板:
template<typename T>
void
func(typename T::type* arg);
Run Code Online (Sandbox Code Playgroud)
以及如果其他功能的扣减失败将使用的"后备":
template<typename>
void
func(...);
Run Code Online (Sandbox Code Playgroud)
和这样的类模板:
template<typename T>
struct A
{
typedef T* type;
};
Run Code Online (Sandbox Code Playgroud)
的调用func<A<int&>>(nullptr)将替换A<int&>为T与以检查是否T::type存在,它必须实例A<int&>.如果我们想象在调用之前放置一个显式的实例化func<A<int&>(nullptr):
template class A<int&>;
Run Code Online (Sandbox Code Playgroud)
那会失败,因为它试图创建类型int&*和指向引用的指针是不允许的.我们不会检查替换是否成功,因为实例化存在硬错误A<int&>.
现在让我们说明确的专业化A:
template<>
struct A<char>
{
};
Run Code Online (Sandbox Code Playgroud)
调用func<A<char>>(nullptr)需要实例化A<char>,因此想象一下在调用之前程序中某处的显式实例化:
template class A<char>;
Run Code Online (Sandbox Code Playgroud)
这个实例化没问题,没有错误,所以我们继续进行参数替换.工作的实例化A<char>,但A<char>::type不存在,但是没关系,因为它只在声明中引用func,因此只会导致参数推断失败,...而是调用回退函数.
在其他情况下,替换可能导致特殊成员函数被隐式定义,可能被删除,这可能会触发其他实例化或隐式定义.如果在"生成实例化和隐式定义"阶段期间发生错误,那么它们就是错误,但如果成功但在替换期间,函数模板签名中的表达式证明是无效的,例如因为它使用不存在的成员或被隐式定义为删除的东西,这不是错误,只是演绎失败.
所以我使用的心智模型是替换需要先做一个"准备"步骤来生成类型和成员,这可能会导致硬错误,但是一旦我们完成了所有必要的生成,任何进一步的无效使用都不是错误.当然,所有这一切都是将问题从" 直接背景意味着什么?"中解决出来.to"在检查此替换之前需要生成哪些类型和成员?" 所以它可能会或可能不会帮助你!
的直接背景基本上是什么,你在模板声明本身看到的.除此之外的一切都是一个难以理解的错误.硬错误示例:
#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);
}
Run Code Online (Sandbox Code Playgroud)