替换在模板实参演绎中是如何工作的

How does substitution work in template argument deduction?

本文关键字:何工作 工作 实参 演绎 替换      更新时间:2023-10-16

c++标准14.8.2$7规定:

替换发生在函数类型和模板形参声明中使用的所有类型和表达式中。这些表达式不仅包括常量表达式(如出现在数组边界或作为非类型模板参数的常量表达式),还包括sizeofdecltype和其他允许非常量表达式的上下文中的通用表达式(即非常量表达式)。替换按照词法顺序进行,并在遇到导致演绎失败的条件时停止。[注:异常说明中的等效替换仅在异常说明实例化时进行,此时,如果替换导致无效类型或表达式,则程序是病态的。]

标准提供了一个例子:

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不会导致替换失败吗?模板函数fg有什么区别?

关键是,首先,

替换按照词法顺序进行,当满足条件时停止遇到导致演绎失败的

其次,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,这会导致硬错误。