一、函数模板
1、先看一个简单的模板函数定义
/*
*在标准c++98添加typename关键字之前,使用class 来创建模板,之后用typename 替代了class
* c++98 之前的模板定义定义为: template < class AnyType> void fun(){...}
*/
template <typename AnyType>
void Swap(AnyType &a, AnyType &b) {
AnyType temp;
temp = a;
a = b;
b = temp;
return;
}
这里继续给出调用代码:
int main()
{
int a = 10, b = 20;
double c = 10.1, d = 10.2;
Swap(a, b);
Swap(c, d);
std::cout << "a = " << a << " b=" << b<< std::endl;
std::cout << "c = " << c << " d=" << d << std::endl;
system("pause");
return 0;
}
执行结果:
a = 20 b=10
c = 10.2 d=10.1
请按任意键继续. . .
2、模板函数的重载
需要多个不同的类型使用同一种算法时,可使用模板。然而,并非所有的类型都使用相同的算法,为了满足需求,可以像重载常规函数定义那样重载模板定义。
重载模板函数代码如下:
template <typename AnyType>
void Swap(AnyType &a, AnyType &b) {
AnyType temp;
temp = a;
a = b;
b = temp;
return;
}
template <typename AnyType >
void Swap(AnyType *a, AnyType *b,int n) {
AnyType temp;
for (int i = 0; i < n; i++) {
temp = a[i];
a[i] = b[i];
b[i] = temp;
}
return;
}
第二个函数作用是交换数组。继续给出函数的调用,以及显示函数show();
/*用来显示数组内容*/
template <typename AnyType>
void Show(AnyType *a, int n) {
for (int i = 0; i < n; i++) {
std::cout << " " << a[i];
}
std::cout << std::endl;
}
int main()
{
int a = 10, b = 20;
double c = 10.1, d = 10.2;
int aa[5] = { 0,1,2,3,4 };
int bb[5] = { 4,3,2,1,0 };
Swap(a, b);
Swap(c, d);
Swap(aa, bb,5);
std::cout << "a = " << a << " b=" << b<< std::endl;
std::cout << "c = " << c << " d=" << d << std::endl;
std:: cout << "aa = ";
Show(aa, 5);
std::cout << "bb =";
Show(bb, 5);
system("pause");
return 0;
}
执行结果:
a = 20 b=10
c = 10.2 d=10.1
aa = 4 3 2 1 0
bb = 0 1 2 3 4
请按任意键继续. . .
3、模板的局限性
通过上述代码我们可以看到,模板函数可能会遇到无法处理的某些类型。比如我们不同通过第一个模板函数用来交换数组。再比如,我们不能为一些结构或者类来使用=号进行交换(如果没有重写赋值运算符符的话)。这里c++提供2中解决方案:
第一种:重载运算符,第二种:显示具体化。
4、显示具体化
c++98标准选择了下面的方法:
- 对于给定的函数名,可以有非模板函数、模板函数和显示模板函数以及他们的重载版本。
- 显示具体化的原型和定义应以template<>打头,并通过名称来指出类型。
- 具体化优先于常规模板,而非模板优先于具体化和常规模板。
假设有结构体
struct job
{
char name[40];
double salary;
int floor;
};
给出非模板函数、模板函数、具体化模板函数的声明:
/*非模板函数*/
void Swap(job &, job &);
/*常规模板函数*/
template <typename T>
void Swap(T &, T &);
/*具体化模板函数*/
template<> void Swap<job>(job &, job &);
这里具体化模板函数中的< job >是可选的,因为参数列表中已有类型的声明。即可以也可以写作:template<> void Swap(job &, job &);
附上测试代码:
#include "stdafx.h"
#include <iostream>
struct job
{
char name[40];
double salary;
int floor;
};
/*常规模板函数*/
template <typename T>
void Swap(T &, T &);
/*具体化模板函数*/
template<> void Swap<job>(job &, job &);
void Show(job & j);
int main() {
using namespace std;
cout.precision(2);
cout.setf(ios::fixed, ios::floatfield);
int i = 10, j = 20;
cout << " i,j = " << i << " , " << j << " . \n";
cout << " 使用 template <typename T> void Swap(T & ,T &) 函数" << endl;
Swap(i, j);
cout << " Now i,j = " << i << " ," << j << ". \n";
job sue = { "Susan Yaffee",7300.60,7 };
job sindney = { "Sidney Tafee",78060.72,9 };
cout << "Before job swapping : \n";
Show(sue);
Show(sindney);
Swap(sue, sindney);
cout << " after job swapping: \n";
Show(sue);
Show(sindney);
system("pause");
return 0;
}
/*常规模板函数*/
template <typename T>
void Swap(T & a, T & b) {
T temp;
temp = a;
a = b;
b = temp;
}
/*
*具体化模板函数
*这个函数值交换job中的 salary 和 floor
*/
template<> void Swap<job>(job & a, job & b) {
double t1;
int t2;
t1 = a.salary;
a.salary = b.salary;
b.salary = t1;
t2 = a.floor;
a.floor = b.floor;
b.floor = t2;
}
void Show(job & j) {
using namespace std;
std::cout << j.name << " : $" << j.salary << " on floor " << j.floor << endl;
}
执行结果:
i,j = 10 , 20 .
使用 template <typename T> void Swap(T & ,T &) 函数
Now i,j = 20 ,10.
Before job swapping :
Susan Yaffee : $7300.60 on floor 7
Sidney Tafee : $78060.72 on floor 9
after job swapping:
Susan Yaffee : $78060.72 on floor 9
Sidney Tafee : $7300.60 on floor 7
请按任意键继续. . .
5、实例化
在代码中包含函数模板本身并不会生成函数定义,它只是一个用于生成函数定义的方案。模板并非函数定义。
c++中有2种模板函数实例化。第一种是隐式实例化(最常用的一种),第二种为显示实例化。
隐式实例化就是,我们上文中用到调用时用到的那样:
int i = 10, j = 20;
Swap(i, j);
这就是隐式实例化,他最终生成了一个 函数定义他的参数类型为int型。即:void Swap(int i,int j){}
显示实例化则需要用 <> 指示出类型 并在声明前加上关键字 template。
//显示实例化
template void Swap<int>(int &, int &);
注意显示具体化声明在关键字template后含有< > 而显示实例化没有
综上:我们见识了隐式实例化,显示实例化和显示具体化。他们都被成为具体化。
他们的相同之处在于,他们表示的都是使用具体类型的函数定义,而不是通用描述。