函数模版

使用 泛型 定义函数,通常把模板放在头文件中

在标准 C++98 添加关键字 typename 之前,是使用关键字 class 来创建模板

1
2
3
4
5
6
7
8
9
10
11
template <typename AnyType> // 许多程序员将 AnyType 简写成 T
// template <class AnyType>
void Swap(AnyType &a, AnyType &b);

void Swap(AnyType &a, AnyType &b)
{
AnyType temp;
temp = a;
a = b;
b = temp;
}

如果是数组,上面写法就不成立

1
2
3
4
5
6
7
8
9
10
11
12
13
template <typename T>
void Swap(T a[], T b[], int n);

void Swap(T a[], T b[], int n)
{
T temp;
for (int i = 0; i < n; i++)
{
temp = a[i];
a[i] = b[i];
b[i] = temp;
}
}

如果只是想交换结构体中部分成员的值,则可以通过 显示具体化函数模板 来重载模版函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 如果直接用 Swap(AnyType &a, AnyType &b),会交换所有成员值
// 使用显示具体化的函数模板
// 告诉编译器,job 类型结构体不使用于 Swap(AnyType &a, AnyType &b)
struct job
{
char name[40];
double salary;
int floor;
};

template <> void Swap<job>(job &j1, job &j2); // <job> 可以不添加
{
double t1;
int t2;

t1 = j1.salary;
j1.salary = j2.salary;
j2.salary = t1;

t2 = j1.floor;
j1.floor = j2.floor;
j2.floor = t2;
}

所以对于函数明,有 非模版函数,模板函数,显示具体化模板函数和以及它们的重载版本

具体化优先于常规模板,非模板函数优先于具体化和常规模板

如果函数调用 Swap(i,j) 导致编译器生成 Swap() 的一个实例,该实例使用 int 类型,模板并非函数定义,但使用 int 的模板实例是函数定义,这种实例化方式被成为 隐式实例化,最初是能通过隐式实例化来使用模板生成函数定义,但现在还允许 显示实例化,比如 template void Swap<int>(int, int);,其中 <> 符号指示类型

与显示实例化不同的是,显示具体化使用下面两个等价的声明之一:

1
2
template <> void Swap<int>(int &, int &); //explicit specialization
template <> void Swap(int &, int &); // explicit specialization

不能在同一个文件(或转换单元)中使用同一种类型的显示实例和显示具体化,因为这是同一种函数定义

1
2
3
4
5
6
7
8
9
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;

使用 Add<ddouble>(x, m) 可强制为 double 类型实例化,并将参数 m 强制转换为 double 类型

隐式实例化、显示实例化和显示具体化统称为 具体化

重载解析,对于函数重载、函数模板和函数模板重载,决定为函数调用哪一个函数定义

假如现在有一个自定义函数 lesser(int x, int y) 和一个模板函数 lesser(T x, T y),如果我们传入的两个形参都是 int 类型,那么会优先调用自定义函数,但我们可以自己选择调用模板函数,lesser<>(m, n),也可以显示实例化 lesser<int>(m, n)