前面已经知道,对于函数模板,编译器利用调用中的函数实参来确定其模板参数。那么下面简单讲述一下模板实参推断。
模板实参推断:从函数实参来确定模板实参的过程称为模板实参推断。在模板实参推断过程中,编译器使用函数中的实参类型来寻找模板实参,用这些模板实参生成的函数版本与给定的函数调用最为匹配。
与非模板函数一样,在一次调用中传递给函数模板的实参被用来初始化对应函数的形参。若该函数使用模板类型参数,那它采用特殊初始化规则,编译器通常不会对实参进行类型转换,而是生成一个新的模板实例。如
long s1,s2;
int i1,i2;
Compare(i1,i2);
Compare(s1,s2);
此处Compare()函数使用的是模板类型的参数,各自声明两个类型的变量,当分别调用Compare()函数时,编译器会生成两个模板以便对应匹配相应调用,而不会对s1,s2进行类型转换,进而只生成一个模板。
编译器一般支持两种类型的转换
1、const转换:可以将一个非const对象的引用(或指针)传递给一个const的引用(或指针)形参。(注:反之不可以)
template <class T>
void example(const T &x,const T &y){}
template <class T>
void example1 (T &x,T &y){}
template <class T>
void example2 (T x, T y) {}
int main()
{
const int x=3;
int y=6;
example(x,y);
example1(x,y); /*不能将const转化非const*/
example2(x,y); /*函数接收非引用类型,形参类型和实例都忽略const*/
}
声明且定义两个变量x,y,将x声明为const类型,则在调用example()模板函数时,可以将实参转换位const类型,但是不可以将非const类型的实参变量转换为非const类型变量,如example1()的调用就是错的。
2、数组或函数指针转换:如果函数形参不是引用类型,则可以对数组或函数类型的实参应用正常的指针转换。一个数组实参可以可以转换为一个指向其首元素的指针。类似的,一个函数实参可以转换为一个该函数类型的指针。如
template <class T>
void test (const T a,const T b){}
template <class T>
void test1 (T &a,T &b){}
int main()
{
int a[10],b[12];
example(a,b);
example1(a,b); /*error*/
}
对于函数,可以使用函数模板对函数指针进行初始化和赋值,如
template <class T>
int example(const T&,constT&);
int (*p)(const int &,const int &)=example;
p的类型是一个指针,指向"接收2个constint&形参并返回int值的函数",形参的类型决定了T的模板实参的类型,T的模板实参为int型,指针p引用的是将T绑定到int的实例化。
函数模板的显式实参
某些情况下,编译器无法推断模板实参的类型(例如 当希望允许用户控制模块实例化,当函数的返回值必须与形参列表中所用的所有类型都不同时)
指定显式模板实参
解决办法可以是定义表示返回类型的第三个模板参数,从而用户控制返回类型,如
template <class T1,class T2,class T3>
T1 example(T2 &x,T3 &y){}
double x=7.5;
int y=4;
example<int>(x,y) /*T1:int T2:double T3:int*/
没有任何函数实参的类型可用来推断T1的类型。每次调用example时调用者必须为T1提供一个显示模板实参。
template <class T>
int Compare(const T &x,const T &y)
{
if (x==y)
return 0;
return (x>y)?1:-1;
}
void example(int (*p)(const string &,const string &)){}
void example(int (*p)(const int &,const int &)){}
void main()
{
example(Compare<int>);
example(Compare<string>);
}
声明模板Compare函数,example函数通过显式指定函数类型分别来调用Compare模板函数,生成不同类型的Compare模板函数,从而实现了用户指定使用类型。