一、模板重载
①和常规重载一样,被重载的模板的函数特征标必须不同。
②并非所有的模板参数都必须是模板参数类型。例如:
template<class T>
void swap(T& a,T& b);
template<class T>
void swap(T* a,T* b,int n);//第三个参数类型为具体类型int,而不是通用类型
二、显示具体化
对于某些类型的参数,我们的算法可能略有不同,比如:对于一个结构体,虽然将一个结构体赋给另一个结构体是允许的,但是,如果我们只想交换一部分成员,那我们需要使用不同的代码,因此我们无法使用模板重载来提供其他的代码。此时,我们可以提供一个具体化函数定义,即显示具体化,其中包含所需的代码。当编译器找到与函数调用匹配的具体化定义时,将使用该定义,而不再寻找模板。
①对于给定的函数名,可以有非模板函数、模板函数和显示具体化模板函数以及它们的重载版本。
②显示具体化的原型和定义应以template<>打头,并通过名称来指出类型。
③具体化将覆盖常规模板,而非模板函数将覆盖具体化和常规模板。例如:
void swap(int&,int&);//非模板函数原型
template<class T>
void swap(T&,T&);//模板原型
template<> void swap<int>(int&,int&);//为int类型显示具体化
如果有多个原型,编译器在选择原型时,非模板版本将优先于显示具体化和模板版本,而显示具体化将优先于使用模板生成的版本。
三、实例化和具体化
记住,在代码中包含函数模板本身并不会生成函数定义,它只是一个用于生成函数定义的方案。编译器使用模板为特定类型函数生成定义时,得到的是模板实例。模板并非函数定义,但使用具体类型的模板实例是函数定义。这种实例化方式被称为隐式实例化,因为编译器之所以知道需要进行定义,是由于程序调用函数时提供了具体类型参数。
最初,编译器只能通过隐式实例化,来使用模板生成函数定义,不过现在C++还允许显示实例化。这意味着可以直接命令编译器创建特定的实例。其句法是,声明所需的种类---用<>符号指示类型,并在声明前加上关键字template:
template void swap<int>(int,int);//显示实例化
实现了这种特性的编译器看到上述声明后,将使用模板生成一个使用int类型的实例。
与显示实例化不同的是,显示具体化使用下面两个等价的声明之一:
template <> void swap<int> (int&,int&);//显示具体化
template <> void swap(int&,int&);//显示具体化
区别在于,这些声明的意思是“不要使用swap()模板来生成函数定义,而应使用独立的、专门的函数定义显示地为int类型函数定义”。这些原型必须有自己的函数定义。显示具体化声明在关键字template后包含<>,而显示实例化没有。
警告:试图在同一个编译单元中使用同一种类型的显示实例化和显示具体化将出错。
隐式实例化、显示实例化和显示具体化统称为具体化。他们的不同之处在于,它们表示的都是使用具体类型的函数定义,而不是通用描述。
四、编译器将使用哪个函数版本
对于函数重载、函数模板和函数模板重载,C++需要一个定义良好的策略,来决定为函数调用使用哪一个函数定义,尤其是有多个参数时。这个过程称为重载解析。过程如下:
①创建候选函数列表。其中包含于被调用函数的名称相同的函数和模板函数。
②使用候选函数列表创建可行函数列表。这些都是参数数目正确地函数,为此有一个隐式转换序列,其中包括实参类型与相应的形参类型完全匹配的情况。
③确定是否有最佳的可行函数。
编译器必须确定哪个可行函数时最佳的。它查看为使函数调用参数与可行的候选函数的参数匹配所需要进行的转换。通常,从最佳到最差的顺序如下:
①完全匹配,但常规函数优先于模板函数。
②提升转换(例如,char和short自动转换为int,float自动转换为double)
③标准转换(例如,int转换为char,long转换为double)
④用户定义的转换,如类声明中定义的转换。
注意:const和非const之间的区别只适用于指针和引用指向的数据。即下面的两个定义将出现二义性错误:
void fun(int);
void fun(const int);
函数模板注意事项