函数模板

假设你想写一个求两数最大值的函数,最初可能会这样写:

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)——由编译器自动生成。

编写步骤

  1. 用占位类型替换需要泛化的实际类型;
  2. 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 课。

命名模板形参

  • 简单场景用单大写字母:TUV
    template <typename T>
    T max(T x, T y) { ... }
    
  • 若用途特殊,可用描述性名称:AllocatorTAllocator

最佳实践

简单占位用单大写字母;有特殊语义或约束时用描述性名称。

模板实例化

使用模板时,编译器将形参替换为实参并生成函数;能否成功取决于类型是否满足模板内操作要求。具体约束应查阅技术文档,如 std::maxCompare 要求。

下节课将展示如何调用模板并实例化具体函数。

关注公众号,回复"cpp-tutorial"

可领取价值199元的C++学习资料

公众号二维码

扫描上方二维码或搜索"cpp-tutorial"