上次,我们看了什么是模板函数,今天,我们就从这个模板函数入手,继而引出一个新的知识点-模板参数自动推导。为了介绍清楚什么是模板参数自动推导,我们先定义几个术语,通过这些术语来描述比较方便,这些术语是:模板形参,模板实参,模板函数形参,模板函数实参。
为了表达明确,我们先来看一下图1,图1中指出了什么是模板参数,模板实参,模板函数形参,模板函数实参。
图1 术语
就像图1所示,template<>中的参数为模板形参,get_max_type()中的参数为模板函数形参。当我们调用这个模板函数的时候,char,int为模板实参,'a',10为模板函数实参。
从图1的代码中,我们可以看到模板的形参和模板函数的形参在位置上,具有一定的对应关系,因此,当我们调用模板函数的时候,C++编译器会自动根据模板函数的实参来推测模板的实参,我们将这种机制成为模板参数自动推导。
根据模板参数自动推导的规则,图1中的代码可以改写为例1,如下:
例1:根据模板参数自动推导,省略模板实参
#include<iostream> using namespace std; template<typename T1, typename T2> int get_max_type(T1 a, T2 b) { int nMax = 0; if (sizeof(T1) >= sizeof(T2)) { nMax = sizeof(T1); } else { nMax = sizeof(T2); } return nMax; } int main(int argc, char * argv[]) { int nMax = get_max_type('a', 10); cout<<"max:"<<nMax<<endl; return 0; }在例1中,因为模板形参T1和T2,在位置上,与模板函数形参a和b,具有一一对应的关系,因此,当我们调用模板函数get_max_type的时候,就可以省略模板实参char和int的输入,使调用模板函数和调用普通函数一样方便。
虽然模板参数自动推导非常方便,但并不是所有的情况都可以使用模板参数自动推导。使用模板参数自动推导需要满足3个条件:
1、模板的形参必须与模板函数的形参在位置上存在一一对应的关系
2、与模板函数返回值相关的模板参数无法进行自动推导
3、需要推导的模板参数必须是连续位于模板参数列表的尾部,中间不能有不可推导的模板参数。
下面,我们通过几个例子来一一说明这3个条件的重要性。
例2:模板的形参与模板函数的形参位置上不存在对应关系
#include<iostream> using namespace std; template<typename T1, typename T2> int get_max_type(char a, int b) { int nMax = 0; if (sizeof(T1) >= sizeof(T2)) { nMax = sizeof(T1); } else { nMax = sizeof(T2); } return nMax; } int main(int argc, char * argv[]) { int nMax = get_max_type('a', 10); cout<<"max:"<<nMax<<endl; return 0; }在例2中,函数模板的形参T1和T2与函数模板的函数形参char和int之间,在位置上,没有任何对应关系,因此在调用这个函数模板的时候,不能进行模板参数自动推导。否则,会出现编译错误,如图2:
图2 vc2013中的编译错误
例3 与函数模板返回值相关的模板参数不能进行自动推导
#include<iostream> using namespace std; template<typename T1, typename T2> T1 get_max_type(char a, T2 b) { int nMax = 0; if (sizeof(T1) >= sizeof(T2)) { nMax = sizeof(T1); } else { nMax = sizeof(T2); } return nMax; } int main(int argc, char * argv[]) { int nMax = get_max_type('a', 10); cout<<"max:"<<nMax<<endl; return 0; }例3中,函数模板形参T2在位置上与函数模板的函数形参b具有一一对应的关系,并且位于模板参数列表的尾部,因此,可以进行自动推导;但是,模板形参T1在位置上与模板的函数形参没有任何对应关系,因此,不能进行自动推导;虽然模板形参T1和模板的函数返回值在位置上具有一一对应关系,但是,仍然不可以。具体编译效果如图3:
图3 vc2013中的编译错误
例4 欲推导的模板参数没有连续位于模板参数列表的尾部,导致中间出现了间隔,所以不能推导。
#include<iostream> using namespace std; template<typename T1, typename T2, typename T3, typename T4> int func(T1 v1, T3 v3, T4 v4) { return 0; } void main() { int nTemp = 0; nTemp = func<, int, int, int>(1, 2, 3); }在例4中,模板参数T1虽然与模板的函数参数v1的类型存在位置上的对应关系,但是由于T2的出现,导致T1不能和T3、T4连续位于模板参数列表的尾部,所以当我们使用这个函数模板的时候,在模板参数的实参-<,int,int,int>中,T2必须填写,这样,如果省略T1,就只剩下了一个‘,’,这样就会导致编译错误;如果连‘,’也不写,即func<int,int,int>,那么,自动推导的参数就是T4,而不是T1了。
今天,我们主要讲解了什么是模板参数自动推导以及自动推导的充要条件,希望大家能够多多实践例子中的代码,加深对模板参数自动推导的理解。