1.2.3 模板参数自动推导
如果仔细观察例1.4中的两次对max_element的调用,就会发现模板参数值与函数调用给出的实参的类型是相关的。
比如第一次调用时给出的模板参数T=int,而调用时给出的实参l的类型是int[],即一个整数数组指针,与const int*等价。同样,第二次调用时模板参数T=char,而给出的实参cl类型也是char[]。对比max_element模板的函数参数列表中形参l的类型为T const *,无需显式指定,完全可以根据调用时的实参类型推导出模板参数值。
实际上,在C++(www.cppentry.com)语言中实现了这一自动推导模板参数值的功能。凡是可以推导出的模板参数“值”,就无需在模板实参列表中写明。因此,例1.4中main函数的两次max_element调用,可以简写成以下形式:
- std::cout << max_element(l, 8) << std::endl;
- std::cout << max_element(cl, 6) << std::endl;
从而使得模板调用看起来与普通函数调用无异,也使代码看起来更整洁。
利用模板参数推导时需要注意以下几点:
编译器只根据函数调用时给出的实参列表来推导模板参数值,与函数参数类型无关的模板参数其值无法推导。
与函数返回值相关的模板参数其值也无法推导。
所有可推导模板参数必须是连续位于模板参数列表尾部,中间不能有不可推导的模板参数。
例1.5是一个简单的程序。
例1.5
- #include <iostream>
-
- template<typename T0,
- typename T1,
- typename T2,
- typename T3,
- typename T4>
- T2 func(T1 v1, T3 v3, T4 v4);
-
- int main() {
-
- double sv2;
-
- using namespace std;
- sv2 = func<double, int, int>(1, 2, 3);
- cout << "\tsv2: " << sv2 << endl;
-
- sv2 = func<double, int, int>(1, 2, 3);
- cout << "\tsv2: " << sv2 << endl;
-
- sv2 = func<double, int, int>(1, 0.1, 0.1);
- cout << "\tsv2: " << sv2 << endl;
-
- sv2 = func<int, double, double>(0.1, 0.1, 0.1);
- cout << "\tsv2: " << sv2 << endl;
- }
-
- template<typename T0,
- typename T1,
- typename T2,
- typename T3,
- typename T4>
- T2 func(T1 v1, T3 v3, T4 v4)
- {
- T0 static sv0 = T0(0);
- T2 static sv2 = T2(0);
-
- std::cout << "\tv1: " << v1
- << "\tv3: " << v3
- << "\tv4: " << v4
- << "\t|| sv0: " << sv0;
- T2 v2 = sv2;
-
- sv0 -= 1;
- sv2 -= 1;
-
- return v2;
- }
在这个简单的程序中首先是一个函数模板func的声明。是的,模板与普通函数及类一样,可以先声明后实现。编译器在处理该模板声明时,已知这个模板接受5个模板参数,依次为T0~T4,其中用于声明函数参数类型的模板参数为T1、T3和T4。T2用于声明函数的返回值类型,T0则与函数参数类型以及返回值类型都不相关。编译器在遇到main函数中对func函数模板的调用时,根本无法从实参类型推导出T0的值,所以T0的值必须在模板实参列表中给定。至于T2,虽然与函数返回值类型相关,粗略一想,似乎可以在某些情况下反推出T2的值,例如:
- double d = func<...>(...);
可否认为T2的值就应该是一个double型呢?其实不然。由于C++(www.cppentry.com)中存在内建自动类型转换机制,double型变量不仅可以接受其他double型变量或常量的赋值,也可以接受诸如int型、char型等多种类型变量常量的值,更何况还有用户自定义的类型转换。如果不在模板参数列表中显式给定函数返回值类型,则以上代码就会产生歧义。况且有的时候函数返回值有可能被忽略。因此函数返回值类型不能根据上下文推断,也就无法自动推导出与函数返回值相关的模板参数值,所以T2的值也必须在调用时给定。又因为T2必须给定,而模板形参与实参之间只能通过位置相关联,因此虽然T1的值可由函数实参类型推导出,也必须在模板实参列表中给出。很遗憾,C++(www.cppentry.com)中并不能支持类似以下的方式来跳过对T1赋值:
- sv0 = func<double, , int>(1, 2, 3); // 语法错误!
所以,例1.5中调用func时,模板实参列表中只可将T3及T4省略,而T0、T1及T2不能省略。