函数模板
1.函数模板的定义
一个可以应用于不同类型的对象的函数叫做函数模板。
函数模板可以让我们在不知道处理的数据是什么类型的情况下进行编程。
2.创建函数模板
template<typename,T>
void function(T &a,T &b)
{
........
}
第一行指出,要建立一个模板,并将类型命名为T。关键字template和typename都是必需的。另外必须使用尖括号。函数模板的原型也必须加上第一行。
3.函数模板的特点
函数模板不能缩短可执行程序,函数模板本身并不会生成函数定义,它只用于生成函数定义的方案。如果以不同的类型调用同一个函数模板,最终在可执行程序中有多个相应的独立函数的定义,就像手工的方式定义了这些函数一样。最终代码不包含任何模板,而只包含模板生成的多个实际函数。使用模板的好处是,他使生成多个函数定义更加简单、更可靠
4. 函数模板的重载
可以向重载常规函数定义那样重载模板定义。和常规重载一样,被重载的模板的函数特征标必须不同。
template<typename,T>
void function(T &a,T &b)
template<typename,T>
void function(T *a,T *b)
template<typename,T>
void function(T *a,T *b,int n;) //并非所有模板参数都必须是模板参数类型
对于模板函数的重载,因为对应的类型是不确定的,所以相对于常规函数,调用模板函数将选择哪一个模板函数将会变得复杂。
5. 显式具体化模板函数
- 对于给定的函数名,可以有非模板函数、模板函数、显式具体化模板函数以及他们的重载版本。
- 显式具体化的原型和定义应以template<>打头,并通过名称来指出类型
- 具体化优先于常规模板,而非模板函数优先于具体化和常规模板。
//非模板函数
void swap(int &a,int &b);
{
int temp;
temp=a;
a=b;
b=temp;
}
//模板函数
template<typename T>
void swap(T &a,T &b);
{
T temp;
temp=a;
a=b;
b=temp;
}
//显式具体化模板函数
struct job{
char name[40];
double salary;
}
template<> void swap<job>(job &a,job &b);
{
double temp;
temp=a.salary;
a.salary=b.salary;
b.salary=temp;
}
则上述函数的函数原型是
//非模板函数
void swap(int &a,int &b);
//模板函数
template<typename T>
void swap(T &a,T &b);
//显式具体化模板函数
templat<> void swap<job>(job &a,job &b);
这样在使用函数
job doctor={"dentist",4500};
job teacher={"math",4000}
swap(teacher,doctor);
swap(teacher,doctor);就不会使用模板函数而使用显式具体化,因为teacher和doctor都是job类型。
6.实例化
调用模板函数使之生成函数定义称之为实例化。
根据调用模板函数传入的参数类型生成的函数定义称之为隐式实例化。
显式实例化
template void swap<int>(int,int);
其语法是,声明所需的种类——用<>符号指示类型,并在声明前加上关键字template。显式具体化声明在template后面包含<>,而显式实例化没有。
显式实例化的使用
方法一:直接显式实例化使用
template<typename T>
T add(T a,T b)
{
return a+b;
}
...
int m=6;
double x=10.2;
cout<<add<double>(x,m)<<endl;
方法二:先显式实例化在使用
template<typename T>
T add(T a,T b)
{
return a+b;
}
...
template double add<double>(double,double); //先实例化
int m=6;
double x=10.2;
cout<<add(x,m)<<endl;
实例化和具体化
隐式实例化、显式实例化和显式具体化统称为具体化。他们的相同之处在于,都是通过模板生成具体的函数定义。
实例化和具体化得区别是实例化只需要声明函数的参数类型就能生成函数定义,显式具体化需要自己写函数定义。
7.编译器对于重载函数的匹配
由于存在函数的重载、模板函数的重载,还有显式具体化、和显式实例化,编译器对于同名函数的的匹配问题就显得比较复杂。
大方向的优先级:常规函数>显式具体化>模板函数
细节方面:
- 指向非const数据的指针和引用优先于非const的指针和引用参数匹配
- 但是const和非const之间的区别只适用于指针和引用指向的数据,也就是说如果有函数 void func(int);和函数 void func(const int)则会出错。
- 两个完全匹配的函数都是模板函数,则较具体的模板函数优先。这意味着显式具体化将优先于使用模板的隐式具体化。
总结:重载解析将寻找最匹配的函数。如果只存在一个这样的函数,则选择它;如果存在多个这样的函数,但其中只有一个是非模板函数,则选择该函数;
如果存在多个合适的函数,且他们都是模板函数,但其中有一个函数比其他更具体,则选择该函数。
如果有多个同样合适的非模板函数或模板函数,但没有一个函数比其他更具体,则函数调用将是不确定的,因此是错误的。