对于函数模板,编译器利用调用中的函数实参来确定其函数模板,从函数实参来确定模板实参的过程就被叫做是模板实参推导。
比如:
1 #include<iostream> 2 #include<string> 3 using namespace std; 4 5 template <class T> 6 int compare(const T &v1, const T &v2) { 7 if (v1 > v2) { 8 cout << "bigger" <<endl; 9 return 0; 10 } 11 else { 12 cout << "small" <<endl; 13 return 1; 14 } 15 } 16 17 template <class T> 18 int compare1(T v1,T v2) { 19 if (v1 > v2) { 20 cout << "compare1::bigger" <<endl; 21 return 0; 22 } 23 else { 24 cout << "compare1::small" <<endl; 25 return 1; 26 } 27 } 28 29 int main() { 30 compare(0, 1); // 调用compare(const int&, const int&) 31 compare(3.14, 2.7); // 调用compare(const float&, const float&) 32 compare(2, 2.7); // error33 34 system("pause"); 35 return 0; 36 }
在模板实参推导过程中,编译器使用函数调用中的实参类型来寻找模板实参,用这些模板实参生成的函数版本与给定的函数调用最为匹配.
一、模板推导规则
模板实参推导还有以下的规则:
1.如果某个函数的多个形参的类型是同一个模板类型形参,推断出来的函数的这些实参类型要完全匹配
就像上面例子上的compare(2, 2.7);一样,必须完成匹配。
2.如果模板函数的形参是非引用类型,忽略const。例:
string s1("a"); const string s2("ab"); compare1(s1, s2); // 调用compare1(string, string)
3.形参如果是const 引用或者指针,实参可以使非const引用或者指针,编译器会自动转换到const。例:
string s1("a"); const string s2("ab"); compare(s1, s2); // 调用compare(const string&, const string&)
4.非引用类型模板形参可以将数组或函数类型的实参转换成数组指针或函数指针。例:
int a[10], b[11]; compare1(a, b); // 调用compare1(int*, int*)
5.引用类型模板形参不能将将数组或函数类型的实参转换成数组指针或函数指针,参数推导将出错。例:
int a[10], b[11]; compare(a, b); // error
6.可以根据函数指针推断模板实参
7.模板实参推断顺序是从函数返回值开始,从左至右逐一推导。例如:
template <typename T1, typename T2, typename T3> T1 sum(T1 t1, T2 t2) { return t1 + t2; } auto x = sum<int, int, int>(1, 2);
但需要注意的是:匹配是从左往右进行的,只有尾部(最右)参数的显示模板实参才可以省略,而且前提是他们可以从函数参数推断出来。
int i = 1; auto x = sum<int, int>(1, i); // VS2012上有错误,尚不知为何
二、实参类型不同的模板推导
在上面的1中,如果某个函数的多个形参的类型是同一个模板类型形参,推断出来的函数的这些实参类型要完全匹配,否则则要出错。
但是,我们可以使用多个类型的参数来实现推导。例:
1 #include<iostream> 2 #include<string> 3 using namespace std; 4 5 template <typename T, typename B> 6 int compare(const T &v1, const B &v2) { 7 if (v1 > v2) { 8 cout << "bigger" <<endl; 9 return 0; 10 } 11 else { 12 cout << "small" <<endl; 13 return 1; 14 } 15 } 16 17 int main() { 18 long l = 655555; 19 compare(l, 2); // 调用(const long&, const int&) 20 21 system("pause"); 22 return 0; 23 }
此外,我们还可以显示的指定实参的类型,例如:
compare<long, int>(100000, 2);
三、确定模板的返回类型
在1.7的情况中,我们虽然可以确定模板的返回类型,但是开销非常大,于是C++11推出了另外一种用来解决模板返回类型的方法:decltype
template<typename T> auto foo(T x) -> decltype(*x) // 指定返回x的引用类型 { return *x; } int a[3] = {1,2,3}; auto &z = foo(a); // 返回int&