为什么需要模板实参演绎
在某个函数模板的调用中,如果都显式的指定模板实参(比如concat<std::string, int>(s, 3)
),那么会导致代码繁琐。但是,可以借助模板实参演绎过程,自动确定这些所需要的模板实参
演绎的过程
针对一个函数调用,演绎过程会比较调用实参的类型和函数模板对应的参数化类型(即T),然后针对要被演绎的一个或者多个参数,分别推倒出正确的替换。我们应该记住:每个实参-参数对的分析都是独立的。因此,如果最后得出的结论发生矛盾,那么演绎将会失败:
template<typename T>
T const& max(T const &a, T const& b){
return a < b ? a : b;
}
int g = max(1, 1.0);
第一个实参类型是int,因此max()模板的参数T被暂时演绎成T,而第2个推导出来的T是double类型,两者冲突,演绎失败。注意,这里演绎失败了不代表这个程序是无效的,如果有符合演绎的重载程序就是成功的。
即使所有的演绎的模板参数都可以被一致性的确定,演绎过程也可能失败。这种情况就是:函数声明中,进行替换的模板实参可能会导致无效的构造。例如:
template<typename T>
typename T::ElementT at(T const & a, int i){
return a[i];
}
void f(int *p){
int x = at(p, 7);
}
在此,T被演绎成int *
,然而,在返回类型T::ElementT,用int*
来替换T之后,会导致一个无效的C++构造,从而也使得这个演绎过程失败。
实参-参数对是如何匹配的
匹配类型A(来自实参的类型)和参数化类型P(来自参数的声明)
- 如果被声明的参数是一个引用类型(即T&),那么P就是所引用的类型(即T),而A是实参类型。 否则,P是所声明的参数类型,A是实参的类型
- 如果实参的类型是数组或者函数类型,还会发生decay转型,转换为对应的指针类型,同时会忽略高层次的const和volatile限定符
template<typename T> void f(T); // P是T
template<typename T> void g(T &); // P是T
double x[10];
int const i = 7;
f(x); // 非引用参数:T是double *
g(x); //引用参数: T是doubel[20]
f(i); // 非引用参数: T是int
g(i); //引用参数: T是int const
f(7); // 非引用参数: T是int
g(7); //引用参数: T是int --》 错误:不能把7传递给int&
decay是一个概念,指的是从数组和函数类型到指针类型的隐式转换
一般来讲,对于引用参数,绑定到该参数的实参是不会进行decay的。但是如果实参是字符串类型,结果就可能不同了。
template<typename T>
T const& max(T const &a, T const& b);
对于max("Apple", "Pear")
, "Apple"的类型是char const[6], "Pear"是const char[5],不存在数组到指针的decay(因为要演绎的参数是引用参数);因此会演绎失败