一、模板基础
1. 函数模板
什么是模板? 简单来说,模板就是一个模具。往里面倒入不同的材料(类型),就能获得不同材料的铸件(具体代码)。
比如实现一个交换函数很容易:
void swap(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
但这只能处理整型。如果想交换浮点数或字符呢?虽然可以通过函数重载解决,但代码重复度高,维护成本大。这暴露了传统方法的两个缺陷:
- 代码复用率低
- 可维护性差
于是有了函数模板,它是泛型编程的基础。
泛型编程:编写与类型无关的通用代码,是提升代码复用率的重要手段。
定义格式如下:
template<typename T>
void Swap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
这里的 typename 也可以换成 class,两者在模板参数声明中通常等价,细微差别会在后续深入讲解。函数模板代表了一个函数家族,使用时根据实参类型被参数化,生成特定版本。
2. 函数模板原理
函数模板本身不是函数,而是编译器生成具体函数的蓝图。本质上,模板将原本需要手动重复的工作交给了编译器。在编译阶段,编译器会根据传入的实参类型推演并生成对应类型的函数实例。
3. 函数模板实例化
使用不同类型参数调用模板时,称为实例化。分为两种:
- 隐式实例化:让编译器根据实参自动推演类型。
int a = 1, b = 2; Swap(a, b); // 编译器推导出 T 为 int - 显示实例化:显式指定模板参数类型。
Swap<int>(a, b); // 强制指定 T 为 int
4. 匹配原则
- 非模板函数和同名函数模板可以共存。如果条件相同,优先调用非模板函数。
- 若模板能产生更好匹配的函数,则选择模板。
- 注意:模板函数不允许自动类型转换,普通函数可以。这意味着调用时必须严格匹配或显式转换。


