1、简单函数模板
函数模板是通用的函数描述,也就是说他们使用通用类型来定义函数,其中的通用类型可用具体的类型替换。通过将类型作为参数传递给模板,可使编译器生成该类型的函数。
比如我们定义了一个函数,函数实现交换两个int变量的值。
void swap(int &a, int &b);
如果我们要实现一个交换两个double变量的值,如何做呢,我们可以重新复制一份代码,更改参数类型和实现完成函数重载。若要交换两个char类型的值,同样复制一份代码。这样做很麻烦,还有可能出错。这里我们可以使用模板的方式来实现上述功能。
模板的形式如下:
template <class Any>
void swap(Any &a, Any &b)
{
Any temp;
temp = a;
a = b;
b = temp;
}
第一行指出,要建立一个模板,并将类型名命名为Any。关键字template和class是必需的,class也可以使用typename来代替。另外,必须使用尖括号。类型的名字可以任意选择。余下的代码则描述如何交换两个Any值。这里要注意的是,模板并不会创建任何函数,而只是高速编译器如何定义函数。需要交换int类型的函数时,编译器将按照模板模式创建这样的函数,并用int代替Any。需要交换double类型的函数时,编译器将按照模板模式创建这样的函数,并用double代替Any。
我们来写个简单的例程
#include <iostream>
template <class any>
void Swap(any &a, any &b);
int main()
{
using namespace std;
int i = 10;
int j = 20;
cout << "i = " << i << " j = " << j << endl;
cout << "after Swap" << endl;
Swap(i,j);
cout << "i = " << i << " j = " << j << endl;
double x = 1.1;
double y = 9.9;
cout << "x = " << x << " y = " << x << endl;
cout << "after Swap" << endl;
Swap(x,y);
cout << "x = " << x << " y = " << y << endl;
}
template <class any>
void Swap(any &a, any &b)
{
any tmp;
tmp = a;
a = b;
b = tmp;
}
执行结果
2、重载的模板
模板也是可以重载的,可以像重载常规函数定义那样重载模板定义。和常规的重载一样,被重载的模板的函数特征标必须不同。如上面的例子中,假如我们要交换并不仅仅是一个数字,而是一个数组呢?
#include <iostream>
template <class any>
void Swap(any &a, any &b)
{
any tmp;
tmp = a;
a = b;
b = tmp;
}
template <class any>
void Swap(any a[], any b[], int n)
{
any temp;
int i;
for(i = 0; i < n; i++) {
temp = a[i];
a[i] = b[i];
b[i] = temp;
}
}
#define NUM 8
void Show(int a[])
{
using namespace std;
cout << a[0] << a[1] << "/";
cout << a[2] << a[3] << "/";
for (int i = 4; i < NUM; i++)
cout << a[i];
cout << endl;
}
int main()
{
using namespace std;
int i = 10;
int j = 20;
cout << "i = " << i << " j = " << j << endl;
cout << "after Swap" << endl;
Swap(i,j);
cout << "i = " << i << " j = " << j << endl;
double x = 1.1;
double y = 9.9;
cout << "x = " << x << " y = " << x << endl;
cout << "after Swap" << endl;
Swap(x,y);
cout << "x = " << x << " y = " << y << endl;
int d1[NUM] = {0,1,2,1,2,0,1,9};
int d2[NUM] = {0,1,0,1,1,9,7,0};
Swap(d1,d2,NUM);
Show(d1);
Show(d2);
3 显示具体化
假设定义了如下结构
#include <iostream>
struct job
{
char name[40];
double salary;
int floor;
}
假设希望交换两个这种结构的内容,原来的模板使用下面的代码来完成交换
temp = a;
a = b;
b = temp;
c++允许一个结构赋值给另一个结构,因此即使any是一个job结构,上述代码也是适用的。但是,假如我们只想交换salary和floor成员呢?则需要使用不同的代码,但是Swap的参数仍然是一致的(两个job结构体的引用),因此无法使用模板重载的方法来提供其他的代码
不过,可以 提供一个具体化函数定义---称为显式具体化。
- 对于给定的函数名,可以有非模板函数,模板函数和显式具体化函数以及他们重载的版本
- 显式具体化的函数的原型和定义应以template<>打头,并通过名称来指出类型
- 具体化将覆盖常规模板,而非模板函数将覆盖具体化和常规模板,也就是说,编译器会优先寻找非模板函数,在寻找显式具体化函数,最后是常规模板函数
对于三种函数的形式,形如
/*非模板函数*/
void Swap(job & a, job &b);
/*普通模板*/
template <class any>
void Swap(any &a, any &b);
/*显式具体化*/
template <> void Swap<job> (job &a, job &b);
显式具体化中Swap<job>中的job是可以省略的
来看一个例子
#include <iostream>
struct job
{
char name[40];
double salary;
int floor;
};
template <class any>
void Swap(any &a, any &b)
{
any tmp;
tmp = a;
a = b;
b = tmp;
}
template <class any>
void Swap(any a[], any b[], int n)
{
any temp;
int i;
for(i = 0; i < n; i++) {
temp = a[i];
a[i] = b[i];
b[i] = temp;
}
}
template <> void Swap (job &a, job &b)
{
double t1 = a.salary;
a.salary = b.salary;
b.salary = t1;
int t2 = a.floor;
a.floor = b.floor;
}
int main()
{
using namespace std;
int i = 10;
int j = 20;
cout << "i = " << i << " j = " << j << endl;
cout <<"after swap" << endl;
swap(i,j);
cout << "i = " << i << " j = " << j << endl;
job xiaoming = {"liming",10000,2};
job xiaohong = {"lihong",20000,3};
cout << xiaoming.name << "salary is " << xiaoming.salary << " on floor " << xiaoming.floor << endl;
cout << xiaohong.name << "salary is " << xiaohong.salary << " on floor " << xiaohong.floor << endl;
cout <<"after swap" << endl;
Swap(xiaoming,xiaohong);
cout << xiaoming.name << "salary is " << xiaoming.salary << " on floor " << xiaoming.floor << endl;
cout << xiaohong.name << "salary is " << xiaohong.salary << " on floor " << xiaohong.floor << endl;
return 0;
}
执行结果
要注意的一点是模板的显式具体化不能单独存在,而是作为模板的补充存在的。并且显式化的声明和定义要放在普通模板之后,它是普通模板的补充
4 实例化和具体化
为进一步了解模板,必须理解实例化和具体化。
模板本身不会生成函数定义,只有在函数调用时,编译器才会根据模板生成函数定义。这种方式是隐式实例化。还有显式实例化,也就是可以直接命令编译器创建特定的实例,句法是,声明所需的种类——用<>符号表示类型,并在声明前加上关键字template
template coid Swap<int>(int,int)
编译器看到上述声明后,将使用Swap()模板生成一个使用int类型的实例。
现在我们将上述几种情况总结一下
...
template <class any>
void Swap (any &, any &); //通用模板
template <> void Swap<job> (job &, job &); //具体化
int main()
{
template void swap<char> (char &, char &); //显式实例化
short a,b;
Swap(a,b); //使用通用模板,隐式实例化
job n,m;
Swap(n,m); //使用具体化
char i,j;
Swap(i,j); //使用显式实例化
}