概念
函数模板是通用的函数描述,即使用泛型来定义函数,其中的泛型可用具体的类型替换。通过将类型作为参数传递给模板,可是编译器生成该类型的函数。
优点
函数模板功能能自动完成这一过程,可以节省时间,而且更可靠。
例子
交换模板
template <typename AnyType>//可以使用class代替typename
void Swap(AnyType &a,AnyType &b)
{
AnyType temp;
temp = a;
a = b;
b = temp;
}
注意:
1.如果需要多个将同一种算法用于不同类型的函数,请使用模板。如果不考虑向后兼容的问题,并愿意键入较长的单词,则声明类型参数时,应使用关键字typename而不是class。
2.函数模板不能缩短可执行程序,最终代码不包含任何模板,只包含位程序生成的实际函数,即使用模板只是减少了手动敲的代码。
重载的模板
被重载的模板的函数特征标必须不同。
template <typename T> //original template
void Swap(T &a,T &b);
template <typename T> //new template
void Swap(T &a,T &b,int n);
显示具体化
具体化函数定义——称为显式具体化(explicit specialization),当编译器找到与函数调用匹配的具体化定义时,将使用该定义,而不再寻找模板。
第三代具体化标准:
- 对于给定的函数名,可以有非模板函数,模板函数和显式具体化模板函数以及他们的重载版本。
- 显示具体化的原型和定义应以template<>打头,并通过名称来指出类型。
- 具体化优先于常规模板,而非模板函数优先与具体化和常规模板。
struct job
{
char name[40];
double salary;
int floor;
}
//non template fuction prototype !!!
void Swap(job &,job &);
//explicit specialization for the job type !!
template <> void Swap<job>(job &,job &);
//template prototype !
template <typename T>
void Swap(T &,T &);
Swap<job>中的<job>是可选的,因为函数的参数类型表明,这是job的一个具体化。因此,该原型也可以这样写
template <> void Swap(job &,job &);
实例化和具体化
- 隐式实例化:
在代码中包含函数模板本身并不会生成函数定义,他只是一个用于生成函数定义的方案。编译器使用模板为特定类型生成函数定义时,得到的是模板实例(instantiation)。这种实例化方式被称为隐式实例化(implicit instantiation)。
- 显式实例化:
template void Swap<int>(int,int);//explicit instantiation
此声明将使用Swap()模板生成一个使用int类型的实例。与显示具体化不同的是,显示具体化不适用Swap()模板来生成函数定义,而是用专门为int类型显式定义的函数定义。
在格式上,显示具体化声明在关键字template后有<>,而显式实例化没有。
注意:在同一个文件(或转换单元)中使用同一类型的显示实例和显示具体化将出错。
- 隐式实例化,显式实例化和显式具体化统称为具体化(specialization)。他们的相同之处在于,他们表示的都是使用具体类型的函数定义,而不是通用描述。
重载解析
对于函数重载,函数模板和函数模板重载,C++需要一个定义良好的策略,来决定为函数调用使用哪一个函数定义,尤其是有多个参数时。这个过程称为重载解析(overloading resolution),
步骤
- 创建候选函数列表。其中包含与被调用函数的名称相同的函数和模板函数。
- 使用候选函数列表创建可行函数列表。这些都是参数数目正确的函数,为此有一个隐式转换序列,其中包含实参类型与相应的形参类型完全匹配的情况(包括实参形参类型相同和实参类型可以隐式转换为形参类型,若需要显示的强制类型转换则不行)。
- 确定是否有最佳的可行函数。如果有,则使用他,否则该函数调用出错。
编译器查看为使函数调用参数与可行的候选函数的参数匹配所需要进行的转换从而确定最佳的可行函数。从最佳到最差的顺序如下:
- 完全匹配,但常规函数优先于模板。
- 提升转换(如char,short->int;float->double)
- 标准转换(如int->char;long->double)
- 用户定义的转换,如类声明中定义的转换。