为什么需要模板?
在C语言中我们如果要交换两个数,比如:
int main()
{
int a; int b;
double c; double d;
}
因为C语言不支持函数重载,如果要交换a和b ,c和d我们需要写两个交换函数temp去完成。
void _swapint(int *pa,int*pb)
{
int temp;
temp = *pa;
*pa = *pb;
*pb = temp;
}
void _swapdouble(double *pc, double*pd)
{
double temp;
temp = *pc;
*pc = *pd;
*pd = temp;
}
如果有更多的类型的对象需要交换,还需要写更多的swap函数来完成。这样显得比较繁琐。
而在c++中的标准库就有一个swap函数可以直接使用使用,并且不用传地址。
swap(a,b)
swap(c,d)
在c++的库中如何完成的呢?有人会说,c++支持函数重载嘛,它可以写多个swap,但是这样会导致数据冗余。
c++提出了泛型编程
他可以解决写多个函数的问题,但是函数逻辑的相同,代码中类型不一样。泛型针对的是广泛类型的代码。
namespace change
{
template <class X>//声明一个类型
void swap(X&a,X&b)
{
X temp = a;
a = b;
b = temp;
}
}
int main()
{
int a = 1; int b = 0;
double c = 2.2; double d = 3.3;
change::swap(a, b);//测试
change::swap(c, d);
return 0;
}
针对不同的数据类型,模板可以推演出T的类型,然后进行模板的实例化,根据推演出的类型生成相应的代码,来进行一个操作。实际上执行上面的交换操作的是模板实例化的两个函数。这一过程发生在代码的编译阶段。
但是我们在操作的时候可能会有这种情况:
swap(a, c)//a和c不是同一个类型,就无法执行,我们该如何解决,
swap(a, (int)b);//通过强转来让他们的类型保持一致
当然我们还有另一个办法,我们知道在实例化的时候是编译器自己去推演,来完成实例化,这就是隐式实例化。我们也可以自己去实例化,比如:
<int>swap(a, c)//通过指定X的类型;来完成显式实例化
我们继续来看
template <classT>
void lu(size_t n)
{
T *p=new T[n];//开辟了一个数组。
delete []p;
}
//针对这种不知道类型的,我们在调用的时候必须显式实例化
模板的匹配原则
一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
// 专门处理double的减法函数
double mus(double left, double right)
{
return left -right;
}
template<class T>
T mus(T left, T right)
{
return left -right;
}//专门处理减法的模板
void main()
{
mus(1, 2); // 与非模板函数匹配,编译器不需要特化
mus<int>(1, 2); // 调用编译器特化的mus版本
}
对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。
// 专门处理double的减法函数
double mus(double left, double right)
{
return left -right;
}
template<class T>
T mus(T left, T right)
{
return left -right;
}//专门处理减法的模板
void main()
{
mus(1, 2); // 与非函数模板类型完全匹配,不需要函数模板实例化
mus(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据s实参生成更加匹配的mus函数
}
如果模板可以产生一个具有更好匹配的函数, 那么将选择模板模板函数不允许自动类型转换,但普通函数可以进行自动类型转换