函数模板是通用的函数描述,它们使用范型来定义函数,其中的范型可用具体的类型(如int或double)替换。
比如,实现一个交换模板
1 template <typename AnyType> 2 void Swap(AnyType &a,AnyType &b) 3 { 4 AnyType temp; 5 temp=a; 6 a=b; 7 b=type; 8 }
当交换两个int类型的值时,编译器奖按模板模式创建函数,并用int代替AnyType。同样,当a、b为double类型时,编译器创建的函数也会使用double。
例子中,template和typename是必须的(typename可用class代替,但是为了区别类与模板,通常使用typename)。类型名可以任意选择,只要遵循c++命名规则即可。
函数模板同样可以重载,类似重载常规函数定义。
和常规重载一样,被重载的模板的函数特征必须不同。比如对第一个交换模板进行重载
1 template <typename AnyType> 2 void Swap(AnyType *a,AnyType *b,int n) 3 { 4 AnyType temp; 5 temp=a[n]; 6 a[n]=b[n]; 7 b[n]=temp; 8 }
重载的函数模板中,最后一个参数的类型为具体类型,而不是范型。
模板有其局限性,编写的模板有可能无法处理某些类型,比如,下面的代码假定定义了赋值,但如果T为数组,这种假设将不成立
1 //定义模板函数 2 template <typename T> //or template <class T> 3 void f(T a,T b) 4 {...} 5 6 //当T为数组,下面代码不成立 7 a=b 8 9 //假设定义了运算符>,但如果T为结构,该假设不成立 10 if(a>b) 11 12 //为数组名定义了运算符>,但由于数组名为地址,因此比较的是数组的地址。所以,当T为数组、指针或结构,这种假设不成立 13 T c=a*b;
另一方面,有时候通用化是有意义的,但c++语法不允许。例如,两个包含坐标位置的坐标结构相加是有意义的,虽然没有为结构定义运算符+。
一种解决方案是,c++允许重载运算符+,以便能够将其用于特定的结构或类。这样使用运算符+的模板便可以处理重载了运算符+的结构。
另一种解决方案是,为特定类型提供具体化的模型定义。
可以提供一个具体化函数定义——成为显示具体化,其中包含所需的代码。当编译器找到与函数调用匹配的具体化定义是,将使用该定义,而不再寻找模板。
假设定义了如下结构
struct job { char name[40]; double salary; int floor; }
假设希望能够交换两个这种结构的内容,原来的模板可以完成。假设只想交换salary和floor成员,而不交换name成员,则需要使用不同的代码,但Swap()的参数将保持不变(两个job结构的引用),因此无法使用模板重载来提供其他代码。
下面分别是非模板函数、模板函数和具体化的原型
1 //non template function prototype 2 void Swap(job &,job &); 3 4 //template prototype 5 template <typename T> 6 void Swap(T &,T &); 7 8 //explicit specialization for the job type 9 template <> void Swap<job>(job &,job &);
在代码中包含函数模板本身并不会生成函数定义,它只是一个用于生成函数定义的方案。编译器使用模板为特定类型生成函数定义是,得到的是模板实例。例如,函数调用Swap(i,j)导致编译器生成Swap()的一个实例,该实例使用int类型。模板并非函数定义,但模板实例是函数定义。
编译器选择使用哪个函数版本
1、创建候选函数列表。其中包含与被调用函数的名称相同的函数和模板函数。
2、使用候选函数列表创建可行函数列表。这些都是参数数目正确的函数,为此有一个隐式转换序列,其中包括实参类型与相应的形参类型完全匹配的情况。
3、确定是否有最佳可行函数。
看得不是很懂,未完待续