替换在模板参数推导中如何工作?
How does substitution work in template argument deduction?
C++ 标准 14.8.2$7 说:
The substitution occurs in all types and expressions that are used in the function type and in template parameter declarations. The expressions include not only constant expressions such as those that appear in array bounds or as nontype template arguments but also general expressions (i.e., non-constant expressions) inside sizeof
, decltype
, and other contexts that allow non-constant expressions. The substitution proceeds in lexical order and stops when a condition that causes deduction to fail is encountered. [ Note: The equivalent substitution in exception specifications is done only when the exception-specification is instantiated, at which point a program is ill-formed if the substitution results in an invalid type or expression. — end note ]
标准在这里提供了一个例子:
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)
是错误的? trailing-return-type T::X
不会导致替换失败吗?模板函数f
和g
有什么区别?
重点是,第一,
The substitution proceeds in lexical order and stops when a condition
that causes deduction to fail is encountered
其次,A<int>
定义的实例化会触发硬错误,而不是替换失败,因为这会导致实例化格式错误的构造 typename T::X
(T == int
) 在直接上下文之外。 [temp.deduct]/8:
Only invalid types and expressions in the immediate context of the
function type and its template parameter types can result in a
deduction failure. [ Note: The evaluation of the substituted types
and expressions can result in side effects such as the instantiation
of class template specializations and/or function template
specializations, the generation of implicitly-defined functions, etc.
Such side effects are not in the “immediate context” and can result in
the program being ill-formed. — end note ]
对于有问题的模板,在函数类型中代入 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
,这会导致硬错误。
C++ 标准 14.8.2$7 说:
The substitution occurs in all types and expressions that are used in the function type and in template parameter declarations. The expressions include not only constant expressions such as those that appear in array bounds or as nontype template arguments but also general expressions (i.e., non-constant expressions) inside
sizeof
,decltype
, and other contexts that allow non-constant expressions. The substitution proceeds in lexical order and stops when a condition that causes deduction to fail is encountered. [ Note: The equivalent substitution in exception specifications is done only when the exception-specification is instantiated, at which point a program is ill-formed if the substitution results in an invalid type or expression. — end note ]
标准在这里提供了一个例子:
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)
是错误的? trailing-return-type T::X
不会导致替换失败吗?模板函数f
和g
有什么区别?
重点是,第一,
The substitution proceeds in lexical order and stops when a condition that causes deduction to fail is encountered
其次,A<int>
定义的实例化会触发硬错误,而不是替换失败,因为这会导致实例化格式错误的构造 typename T::X
(T == int
) 在直接上下文之外。 [temp.deduct]/8:
Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure. [ Note: The evaluation of the substituted types and expressions can result in side effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such side effects are not in the “immediate context” and can result in the program being ill-formed. — end note ]
对于有问题的模板,在函数类型中代入 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
,这会导致硬错误。