假设你想写一个求两数最大值的函数,最初可能会这样写:
int max(int x, int y)
{
return (x < y) ? y : x;
// 注:使用 < 运算符与 std::max 保持一致
}
调用者只能传入 int
类型(或可提升为 int
的类型)。当需要比较两个 double
时,必须再写一份重载:
double max(double x, double y)
{
return (x < y) ? y : x;
}
两份实现除类型外完全相同。为每种类型都写一份重载,既繁琐又易出错,还违反 DRY(Don’t Repeat Yourself)原则;且无法覆盖未来可能出现的新类型。
我们需要一份代码即可适用于任意类型。普通函数难以胜任,而 C++ 提供的模板机制正是为此而生。
C++ 模板简介
模板系统旨在简化“跨类型通用”函数或类的编写。我们不再手工编写大量仅类型不同的重载,而是写一份模板:
- 模板像普通定义一样描述函数/类的结构;
- 不同之处在于使用占位类型(类型模板形参,俗称模板类型)代替具体类型;
- 实际类型由调用者在使用模板时指定;
- 编译器根据模板自动生成所需的重载函数或类。
关键洞察
一份模板可生成一整族类型各异而逻辑相同的函数或类;实际类型直到使用模板时才确定,因此模板可兼容尚未存在的类型,既灵活又具未来扩展性。
类比
模板如同“绘图模板”(stencil):一次制作,可反复使用;颜色(实际类型)在使用时才决定,模板无需预知所有可能的颜色。
函数模板
函数模板是生成一组重载函数的“函数式”定义:
- 主模板(primary template)——编写一次;
- 实例化函数(instantiated function)——由编译器自动生成。
编写步骤
- 用占位类型替换需要泛化的实际类型;
- 用
template <typename T>
声明模板形参。
示例:将 max(int, int)
改为模板
template <typename T> // 模板形参声明
T max(T x, T y) // 函数模板定义
{
return (x < y) ? y : x;
}
template
关键字表明此为模板;typename T
(或class T
)声明类型模板形参T
;- 作用域仅限于该模板;
- 每个模板需独立声明。
相关内容
多模板类型函数模板见 11.8 课。
命名模板形参
- 简单场景用单大写字母:
T
、U
、V
:template <typename T> T max(T x, T y) { ... }
- 若用途特殊,可用描述性名称:
Allocator
、TAllocator
。
最佳实践
简单占位用单大写字母;有特殊语义或约束时用描述性名称。
模板实例化
使用模板时,编译器将形参替换为实参并生成函数;能否成功取决于类型是否满足模板内操作要求。具体约束应查阅技术文档,如 std::max
的 Compare
要求。
下节课将展示如何调用模板并实例化具体函数。