- 函数模板是通用的函数描述,是使用泛型来定义的函数。
- 泛型可用具体的类型(如int,double)来替换。
1. 函数模板
1.1 什么是函数模板
- 函数模板允许使用任意的类型来定义函数,例如,可这样建立一个
swap
模板:
template <typename T> // 该句可改为: template <class T>
void swap(T& a, T& b){
T temp;
temp = a;
a = b;
b = temp;
}
- 上述代码第一行中
template
和typename
是必须的,可以使用class
代替typename
(C++98新增关键字typename)。 - 此外,必须使用尖括号
<>
。 - 函数模板的声明声明和定义都需要加
template <typename T>
。
测试代码如下:
#include <iostream>
template <typename T>
void Swap(T& a, T& b); // 1. 模板函数-原型
int main() { // 3. 主函数测试
using namespace std;
int a = 10;
int b = 999;
Swap(a,b);
cout << "a:" << a << ", b:" << b << ".\n";
return 0;
}
template <typename T> // 2. 模板函数-定义
void Swap(T& a, T& b){
T temp;
temp = a;
a = b;
b = temp;
}
a:999, b:10.
1.2 重载的函数模板
- 需要对 多个对不同类型 使用同一种算法的函数时,可以像重载常规函数定义那样 重载模板定义。和常规的重载一样,被重载的模板的函数特征必须不同。
- 与 1.1测试代码 不同,本小结测试代码新增一个模板,其特征为
(T[],T[], int)
。
#include <iostream>
template <typename T>
void Swap(T& a, T& b); // 模板函数 1
template <typename T>
void Swap(T* a, T* b, int n); // 模板函数 2
int main() {
using namespace std;
int a = 10; // 测试 模板函数 1
int b = 999;
Swap(a,b);
cout << "a:" << a << ", b:" << b << ".\n";
int arr[5] = {1,2,3,4,5}; // 测试 模板函数 2
int brr[5] = {11,12,13,14,15};
Swap(arr,brr,5);
cout << "arr: ";
for (auto i:arr)
cout << i << " ";
return 0;
}
template <typename T> // 模板函数 1
void Swap(T& a, T& b){
T temp;
temp = a;
a = b;
b = temp;
}
template <typename T> // 模板函数 2
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;
}
}
a:999, b:10.
arr: 11 12 13 14 15
2. 具体化
2.1 显式具体化
- 当类型为某些无法处理的特殊类型时,我们需要对 这些特殊类型 提供具体化的模板定义。假设有如下结构:
struct job{
char name[40];
double salary;
};
- 上述的结构使用第一节中的模板函数也可以完成交换,
C++
中允许一个结构赋值给另一个。 - 但是,当我们只想交换
salary
成员值时,就需要提供一种具体化函数–称为显式具体化
。
要点总结:
- I. 对于给定的函数名,可以有
非模板函数
、模板函数
和显式具体化模板函数
以及他们的重载。 - II.
显式具体化
的原型和定义应以template<>
打头,并通过名称来指出类型。 - III 编译器在选择原型时的优先级: 非模板 > 具体化 > 模板
void Swap(job& a, job& b); // 1. 非模板函数原型
template<typename T>
void Swap(T& a, T& b); // 2. 模板函数原型
template<> void Swap<job>(job& a, job& b); // 3. 显式具体化
Swap<job>
中的<job>
是可选的,因为函数的参数类型表明,这是job
的一个具体化。- 因此,上述代码中
显式具体化
原型也可以这么写:
template<> void Swap(job& a, job& b); // 3. 显式具体化
测试代码:
#include <iostream>
struct job{
char name[40];
double salary;
};
template <typename T>
void Swap(T& a, T& b); // 常规版本
template <> void Swap<job>(job& a, job& b); // 显式具体化
using namespace std;
int main() {
job job_a = {"mike",100.0}; // 测试 显式具体化
job job_b = {"alex",888.8};
Swap(job_a,job_b);
cout << "job_a: " << job_a.name << ", salary:" << job_a.salary << endl;
cout << "job_b: " << job_b.name << ", salary:" << job_b.salary << endl;
return 0;
}
template <typename T>
void Swap(T& a, T& b){
T temp;
temp = a;
a = b;
b = temp;
}
template <> void Swap<job>(job& a, job& b){
double temp;
temp = a.salary;
a.salary = b.salary;
b.salary = temp;
}
job_a: mike, salary:888.8
job_b: alex, salary:100
2.2 实列化
- 当编译器使用模板为特定的类型生成函数定义时,得到的是
模板实列化
。 - I. 隐式实列化: 如
i、j
为int
类型,调用Swap(i,j);
将导致编译器生成Swap()
的一个实列,该实列使用int
类型, 注意:模板并非函数定义,但使用int的模板实列是函数定义,这种实列化叫隐式实列化
。 - II. 显式实列化: 直接命令编译器创建特定的实列,如
Swap<double>()
,这种实列化成为显式实列化
,
I. 其声明使用<>
来指示类型.
II. 在声明前加关键字template
。
template void Swap<double>(double, double); // 显式实列化
2.3 实列化 与 具体化
- 我们可以看到,显式实列化 与 显式具体化十分相似。
template void Swap<double>(double, double); // 显式实列化
template<> void Swap<double>(double& , double& ); // 显式具体化-1
template<> void Swap(double& , double& ); // 显式具体化-2
- 显式实列化 与 显式具体化二者区别:
- I. 显式具体化意思是不要使用
Swap()
模板来生成函数定义,应该使用专门为double
类型显式定义的函数定义。这些原型必须有自己的函数定义。- II. 显式具体化声明在关键字
template
后有<>
, 而显式实例化没有。- III. 试图在同一个文件中使用同一种类型的 显式实列化 和显式具体化将出错。
- 隐式实列化、显式实列化、显式具体化 统称为
具体化
。他们的相同之处在于,它们表示的都是使用具体类型的函数定义,而不是通用描述。 - 本小节不写例子了,会看起来很乱,下面的代码对 具体化 进行了总结:
...
template <typename T>
void Swap(T&, T&); // 1. 模板原型
template<> void Swap<job>(job&, job&); // 2. 显式具体化
int main(){
template void Swap<char>(char&, char&); // 3. 显式实列化
short a,b;
...
Swap(a,b); // 4. 隐式实列化
job n,m;
...
Swap(n,m); // 使用显式具体化
char g,h;
...
Swap(g,h); // 使用显式实列化
}