默认参数能让你使用不同数目的参数调用同一个函数,而函数多态(函数重载)能让你使用多个同名函数。----一般完成类似的工作,但一定使用不同的参数列表(函数特征标)。
下面定义一组原型如下的print()函数
void print(const char*str,int width);
void print(double d,int width);
void print(long l,int width);
编译器根据参数列表选择相应原型。
1.类型引用和类型本身,const和非const变量(非指针,引用)被视为同一个特征标。
2.实现函数重载时,函数的返回类型可以不同,但特征标也必须不同。
3.若出现参数匹配多个函数版本,调用最匹配的版本。
函数模板是通用的函数描述,它们使用泛型来定义函数,其中泛型可用具体的类型(例如int,double等)替换。下面都将以Swap()函数为例解释相关知识。
例
template
void Swap
关键字typelate,typename必须包含,类型名(这里是AnyType)可以任意选择,一般用T。
typename也可以替换为class,它们是等价的。
编译器将检查所使用的参数类型,并生成相应的函数。
例
template
void Swap(T &a,T &b)
{
T temp;
temp=a;
a=b;
b=temp;
}
template
void Swap(T a[],T b[],int n)
{
T temp;
for(int i=0;i
模板的参数类型不一定都是泛型
隐式实例化
编译器使用模板为特定类型生成函数定义时,得到的是模板实例,这种实例化方式称为隐式实例化。
int m,n;
m=1,n=2;
Swap(m,n);
此时编译器根据提供的参数类型,生成了对应的函数定义。
显式实例化
可以直接命令编译器创建特定的实例,如Swap
template void Swap(int,int);
还可在程序中使用函数来创建显示实例化 。
template
T Add(T a,T b)
{
return a+b;
}
...
int m=6;
double x=10.1;
cout<(x,m)<
模板与函数调用不匹配,因为x,m不是相同类型的变量。但通过使用Add
显式具体化
与实例化不同,该种方法不使用Swap()模板来生成函数定义,而是专门为一个具体类型生成函数定义。两个等价声明为
template <> void Swap(int &,int &);
template <> void Swap(int &,int &);
隐式实例化,显式实例化,显式具体化统称为具体化。它们表示的都是使用具体化类型的函数定义,而不是通用描述。
对于函数重载,函数模板,函数模板重载,C++需要一个好的策略来决定为函数调用使用哪一个函数定义。下面将根据单个参数的函数简要介绍相关原则,通常从最佳到最差的顺序如下所述
1.完全匹配,但常规函数优先于模板。
2.提升转换(例如,char或short自动转换为int,float自动转换为double)
3.标准转换(例如,int转换为char,long转换为double)
4.用户定义的转换,如声明中定义的转换。
例
may('B')//函数调用,参数为char类型
//下面为系列函数原型
void may(int) //#1
float may(float,float=3) //#2
void may(char) //#3
char*may(const char*); //#4
char may(const char&); //#5
template void may(const T &); //#6
template void may(T *); //#7
#4和#7类型不匹配排除。根据原则1,#6为模板优先级低于常规函数。根据原则2和3 char到int为提升转换,char到float为标准转换,#1优于#2。#3,#5,#6都优先于#2,#1,因为它们都是完全匹配的。
匹配时允许一些"无关紧要的转换",在这些转换内都是完全匹配。
从实参 | 到形参 |
Type | Type& |
Type& | Type |
Type[] | *Type |
Type(argument-list) | Type(*)(argument list) |
Type | const Type |
Type | volatile Type |
Type* | const Type |
Type* | voliatile Type* |
两个函数都完全匹配时,仍然可完成重载解析。
越具体不一定针对模板,实际为编译器判断使用哪种类型执行的转换最少。
有时候可以编写合适的函数调用来引导编译器做出你希望的选择。
int a,b;
a=1;b=2;
//下面为函数调用
cout<(a,b)<(a,b)<
#1中<>指出编译器应使用模板函数而不是非模板函数。
#2中指出编译器应使用显式实例化得到的函数。
编写模板函数时,一个问题并非总能预先知道该在声明中使用哪种类型。如下例
template
void ft(T1 x,T2 y)
{
?type? xpy=x+y;//x+y的类型无法确定
}
T1,T2分别是double,int,两个变量的和将为double类型;是short,int,变量的和将为int类型;是short,char,变量的和将为int类型。
C++11新增的关键字decltype提供了解决方案,可以这样使用
int x;
decltype(x) y;//让y的类型与x相同
decltype(x+y) xpy;//让xpy的类型与x+y相同
xpy=x+y
//也可样写
decltype(x+y) xpy=x+y;
确定类型编译器必须遍历一个核对表,简化步骤如下
decltype(expression)var//声明
1.expression是未用括号括起的标识符(变量名,函数名等),则var与该标识符同类型。
2.expression为函数调用,则var类型与返回类型相同。
3.expression是用括号括起的标识符,则var为指向其类型的引用。例如\
double x=4.4;
decltype((x)) r2=x;//r2是指向double变量x的引用
4.前面情况都不满足则var与expression同类型。(例如表达式,右值)
有一相关问题decltype本身无法解决。
template
?type? gt(T1 x,T2 y)
{
return x+y;
}
返回类型无法设置为decltype(x+y),因为此时还未声明x和y。而C++后置返回类型写成这样
double h(int x,float y);
//改用后置返回类型
auto h(int x,float y)->double;
//则上面的gt()函数可改为
template
auto gt(T1 x,T2 y)->decltype(x+y)
{
return x+y;
}
其中auto是一个占位符,表示后置返回类型提供的类型。