模板函数:
函数模板可以用来创建一个通用的函数,以支持多种不同的形参,避免重载函数的函数体重复设计。它的最大特点是把函数使用的数据类型作为参数。是通用函数的描述,泛型定义函数。需注意的是,模板函数不创建任何函数,只是告诉编译器如何编译。也就是说模板函数本身不占任何空间,只有定义了特定类型后才需空间储存这个函数。
为什么使用模板函数:
当使用不同类型参数函数时,能够节省时间修改函数,而且不用手工修改,可靠。当需要多个将同一算法用于不同类型的函数时,模板函数为最好的选择。
声明和使用:
eg.
template <typename AnyType>
void Swap(AnyType &a, AnyType &b)
{
AnyType temp;
temp = a;
a = b;
b = temp;
}
关键字为template和typename(也可以用class)。至于AnyType是表示任何数据类型,由程序员按c++命名规则命名。
重载函数模板:
与普通函数重载相同,也是函数特征值必不同。
eg./*
以下这个例子说明了重载函数模板,程序员可以通过传的参数不同来选择调用交换元素或是交换数组的函数。
而这个例子还说明了另一个问题,看交换数组的函数参数,最后有个int n,这个是指定的具体类型。
*/
template<typename T>
void Swap(T &a, T &b); //用于交换两个元素a,b的值
template<typename T>
void Swap(T *a, T *b, int n); //用于交换两个数组a,b的值,n代表数组的长度
函数模板的局限性
函数模板也有一定的局限性,编写的模板可能无法处理某些类型。
eg.
template<typename T>
void fun(T a, T b);
...
a = b; //若T的类型为数组,则无法成立
...
if(a > b) ... //若T的类型为结构则无法成立
解决方案有两种,一种是通过重载运算符来使这些标点符号符合类型运算。二是为特定类型提供具体化模板定义(显示具体化),即在定义模板后,又为模板制定特定类型所使用的函数。
eg. //这里要交换结构体job的salary和floor而不改变name
struct job
{
char name[40];
double salary;
int floor;
}
template<typename T>
void Swap(T &a, T &b);
...
template<typename T>
template<> void Swap<job>(job &j1, job &j2) //特定为swap指定了job类型的函数
{
//只交换salary和floor,不交换name
double t_sar = j1.sarlary;
int t_flo = j1.floor;
j1.sarlary = j2.sarlary;
j1.floor = j2.floor;
j2.sarlary = t_sar;
j2.floor = t_flo;
}
注意!这里要区分几个概念:
1、隐式实例化:直接交给编译器判断如何定义函数。
2、显式实例化:直接命令编译器创建特定实例。如,Swap<int>();
3、显式具体化:为特定类型提供具体化定义。
eg.
...
template<class T>
void Swap(T &a, T &b);
template<class T>
template<> void Swap<job>(job &j1, job &j2);
...
int main()
{
template void Swap(char &, char &); //显式实例化
char g, h;
Swap(g, h);
double a, b; //显式实例化
Swap<int>(a, b);
short c, d; //隐式实例化
Swap(c, d);
job n, m; //显式具体化
Swap(n, m);
}
函数调用的顺序: 非模板函数 > 具体化模板函数 > 模板函数(较具体的优先)
关于调用函数的过程:
1、创建候选函数列表(名称相同的函数,模板函数)。
2、使用候选函数列表来创建可行函数列表(通过隐式转换,如int->double类型)进行完全匹配。
3、确认是否有最佳可行的函数,若有则使用,最佳匹配。若无,则报错(例如二义性ambiguous)。
完全匹配中的无关紧要转换:参数可以通过隐式转换是因为这个转换无关紧要。
eg.
实参->形参
Type t -> Type &t
Type &t -> Type t
Type []t -> Type *t
Type t -> const Type t
Type *t -> const Type t
关于函数中运算中的类型:若两种不同的类型T1,T2做运算,则得出的结果到底是什么类型呢?
eg.
template<typename T1, typename T2>
/*
void count(T1 a, T2 b)
{
a + b; //如果调用为count<int, double>的话那么这个a+b的值改怎么弄为a+b后的类型呢
}
*/
//这里给出如下方法decltype
void count(T1 a, T2 b)
{
decltype(a+b)ans; //定义了变量ans,让ans的类型为a+b
}
//但如果想要返回这结果呢?用decltype(a+b) count(T1 a, T2 b)可以吗?答案是否定的,因为编译器还不知decltype中的a和b。这里有另一种方法。
auto count(T1 a, T2 b) -> decltype(a+b)
{
...
}
//通过用auto->decltype即可,因为decltype移到(T1 a, T2 b)后面,编译器便知道a和b为何物