替换在模板实参演绎中是如何工作的
How does substitution work in template argument deduction?
c++标准14.8.2$7规定:
替换发生在函数类型和模板形参声明中使用的所有类型和表达式中。这些表达式不仅包括常量表达式(如出现在数组边界或作为非类型模板参数的常量表达式),还包括
sizeof
、decltype
和其他允许非常量表达式的上下文中的通用表达式(即非常量表达式)。替换按照词法顺序进行,并在遇到导致演绎失败的条件时停止。[注:异常说明中的等效替换仅在异常说明实例化时进行,此时,如果替换导致无效类型或表达式,则程序是病态的。]
标准提供了一个例子:
template <class T> struct A { using X = typename T::X; };
template <class T> typename T::X f(typename A<T>::X);
template <class T> void f(...) { }
template <class T> auto g(typename A<T>::X) -> typename T::X;
template <class T> void g(...) { }
void h() {
f<int>(0); // OK, substituting return type causes deduction to fail
g<int>(0); // error, substituting parameter type instantiates A<int>
}
为什么在这里调用g<int>(0)
是错误的?回尾型T::X
不会导致替换失败吗?模板函数f
和g
有什么区别?
关键是,首先,
替换按照词法顺序进行,当满足条件时停止遇到导致演绎失败的
其次,A<int>
定义的实例化会触发一个硬错误,而不是替换失败,因为这会导致在直接上下文之外实例化一个格式错误的构造typename T::X
(与T == int
一起)。(temp.deduct)/8:
对象的直接上下文中只使用无效的类型和表达式函数类型及其模板参数类型可以导致扣除失败。[注意:被替换类型的求值表达式可能会导致实例化等副作用类模板特化和/或函数模板专门化、隐式定义函数的生成等。这样的副作用不是在"即时环境"中,可能导致程序格式不正确。- 结束说明]
对于有问题的模板,在函数类型中替换为typename T::X
会导致推导失败(即SFINAE);代入typename A<T>::X
会导致硬错误。由于替换是按词法顺序进行的,因此对于template <class T> typename T::X f(typename A<T>::X);
,它首先替换为typename T::X
,导致演绎失败,并且不会尝试进一步的替换。另一方面,对于template <class T> auto g(typename A<T>::X) -> typename T::X;
,它首先替换为typename A<T>::X
,这会导致硬错误。
- QSqlquery prepare()和bindvalue()不工作
- 导入库可以跨dll版本工作吗
- 以螺旋方式打印矩阵的程序.(工作不好)
- 对象指针在c++中是如何工作的
- 为什么在Windows上的VS 2019和Clang 9中"size_t"在没有标题的情况下工作
- VSOMEIP-2个设备之间的通信(TCP/UDP)不工作
- 为字符串中每 N 个字符插入空格的函数没有按照我认为的方式工作?
- C++为线程工作动态地分割例程
- 为什么我的 std::ref 无法按预期工作?
- 布尔比较运算符是如何在C++中工作的
- SampleConsensusPrerejective(ext.RANSAC)是如何真正工作的
- 不确定要在我的main中放入什么才能使我的代码正常工作
- 为什么std::condition_variable notify_all的工作速度比notify_one快(对于随机请
- <<操作员在下面的行中工作
- 有人能解释一下为什么下界是这样工作的吗C++的
- ExtractIconEx:可以工作,但偶尔会崩溃
- C++中的memset函数工作不正常
- 当我在第一个循环中使用"auto"时,它工作正常,但是使用"int"它会给出错误,为什么?
- 链表c++插入,所有情况都已检查,但没有任何工作
- 当 int 方法工作正常时,void 方法有何不同,或者为什么我不能调用 void 方法?