跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++算法

C++ 模板初阶:泛型编程基础

C++ 模板是泛型编程的核心工具,允许编写与类型无关的代码。函数模板通过 template 关键字定义,编译器根据实参隐式或显式实例化具体类型。类模板类似,但成员函数在类外定义时需指定类名模板参数。对比了传统重载与模板的差异,解析了模板参数匹配原则及实例化机制,帮助理解如何高效复用代码并避免编译错误。

战神发布于 2026/3/26更新于 2026/10/2584 浏览
C++ 模板初阶:泛型编程基础

前言

C++ 模板是实现泛型编程的核心工具,允许我们编写与类型无关的代码。这不仅能提高复用性,还能在编译时根据实际类型生成具体代码,避免运行时开销。

一、为什么需要模板?

写函数或类时,如果希望处理多种数据类型(比如 int、double),传统做法是函数重载。但这样会产生大量重复代码,或者丢失类型信息。

场景: 需要一个求最大值的函数,支持 int、double 和 string。

1. 传统方法:函数重载

#include <iostream>
#include <string>
using namespace std;

// 为 int 重载
int max(int a, int b) {
    cout << "int version\n";
    return a > b ? a : b;
}

// 为 double 重载
double max(double a, double b) {
    cout << "double version\n";
    return a > b ? a : b;
}

// 为 string 重载
string max(const string& a, const string& b) {
    cout << "string version\n";
    return a > b ? a : b;
}

int main() {
    cout << max(3, 5) << "\n";          // int version
    cout << (, ) << ;     
    cout << (, ) << ; 
     ;
}
max
3.14
2.7
"\n"
// double version
max
"hello"
"world"
"\n"
// string version
return
0

缺点很明显:

  1. 代码重复:每个类型都要写一遍几乎相同的逻辑。
  2. 扩展性差:如果要支持新类型(如 char),必须手动添加新重载。
  3. 维护困难:修改算法(比如改为返回较小值)需要改动所有重载函数。

2. 模板方法:函数模板

模板允许将类型作为参数,编译器会根据调用时传入的具体类型生成对应的代码。

#include <iostream>
#include <string>
using namespace std;

template <typename T>
T max(T a, T b) {
    cout << "template version for " << typeid(T).name() << "\n";
    return a > b ? a : b; // 要求 T 支持 operator>
}

int main() {
    cout << max(3, 5) << "\n";          // T = int
    cout << max(3.14, 2.7) << "\n";     // T = double
    cout << max("hello", "world") << "\n"; // T = string
    cout << max('a', 'b') << "\n";      // T = char
    return 0;
}

优点:

  1. 一份代码适用于所有类型,只要该类型支持 operator>。
  2. 无需单独编写,维护成本低。
  3. 效率相同:编译器生成的代码与手写重载效率一致。

二、函数模板详解

1. 概念与定义

函数模板代表了一个函数家族,在使用时被参数化。定义以 template 关键字开头,后跟模板参数列表。

语法要点:

  • 使用 template <class T> 或 template <typename T>。
  • T 是占位符,可自定义名称,但不能用 struct 代替 class。
  • 多个模板参数时用逗号分隔,例如 template<class T1, class T2>。

示例:交换两个数

#include <iostream>
using namespace std;

template<class T>
void Swap(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}

int main() {
    int m = 10, n = 20;
    double a = 1.2, b = 2.5;
    Swap(m, n);
    Swap(a, b);
    return 0;
}

2. 实例化机制

不同类型的参数使用函数模板时,称为实例化。分为隐式实例化和显式实例化。

隐式实例化

编译器根据实参推演模板参数的实际类型。

template<class T>
T Add(const T& left, const T& right) {
    return left + right;
}

int main() {
    int a1 = 10, a2 = 20;
    double d1 = 10.0, d2 = 20.0;
    Add(a1, a2);   // T 推导为 int
    Add(d1, d2);   // T 推导为 double
    return 0;
}

注意: 如果只有一个模板参数,传入两个不同类型会报错。

int main() {
    int a1 = 10;
    double d1 = 3.14;
    Add(a1, d1); // 编译报错:无法确定 T 是 int 还是 double
    return 0;
}

原因: 编译器看到 Add(a1, d1) 时,通过 a1 将 T 推演为 int,通过 d1 将 T 推演为 double。由于模板参数列表中只有一个 T,冲突导致报错。模板中编译器通常不会进行隐式类型转换,以免掩盖潜在问题。

显式实例化

在函数名后的 <> 中指定模板参数的实际类型。

int main() {
    int a1 = 10;
    double d1 = 3.14;
    
    // 强制指定 T 为 int,d1 会被隐式转换为 int
    Add<int>(a1, d1); 
    
    // 强制指定 T 为 double,a1 会被隐式转换为 double
    Add<double>(a1, d1); 
    return 0;
}

这里告诉编译器:'别推了,就用我指定的类型'。编译器会检查调用是否合法,如果类型不匹配且无法转换,依然会报错。

3. 匹配原则

当存在非模板函数和同名函数模板时,匹配规则如下:

  1. 优先调用非模板函数:如果其他条件都相同,优先调用非模板函数。
  2. 模板更匹配则选模板:如果模板可以产生一个具有更好匹配的函数,选择模板。
// 专门处理 int 的加法函数
int Add(int left, int right) { return left + right; }

// 通用加法函数
template<class T> T Add(T left, T right) { return left + right; }

void Test() {
    Add(1, 2);       // 与非模板函数匹配
    Add(1, 2.0);     // 模板函数生成更匹配的版本 (T=double)
}

三、类模板

1. 概念与定义

类模板不是具体的类,而是'类的蓝图'。它使用模板参数描述成员的类型。

基本语法:

template<class T>
class Stack {
public:
    void push(const T& x);
    T pop();
private:
    T data[100];
    int top;
};

注意: 成员函数如果在类外定义,必须再次声明模板参数,并使用完整的类名 <T> 限定。

template <typename T>
void Stack<T>::push(const T& x) {
    data[++top] = x;
}

template <typename T>
T Stack<T>::pop() {
    return data[top--];
}

2. 实例化

类模板实例化需要在类名后跟 <>,并将类型放入其中。类名本身不是类型,实例化后的结果才是。

template<class T>
class Stack {
public:
    Stack(int n = 4) : _array(new T[n]), _capacity(n), _size(0) {}
    void Push(const T& x);
    ~Stack() { delete[] _array; }
private:
    T* _array;
    size_t _capacity;
    size_t _size;
};

template<class T>
void Stack<T>::Push(const T& x) {
    if (_size == _capacity) {
        int newcapacity = _capacity * 2;
        T* tmp = new T[newcapacity];
        memcpy(tmp, _array, _capacity);
        delete[] _array;
        _array = tmp;
        _capacity *= 2;
    }
    _array[_size++] = x;
}

int main() {
    Stack<int> st1;      // 整形栈
    st1.Push(1);
    st1.Push(2);
    
    Stack<double> st2;   // 浮点栈
    st2.Push(1.1);
    
    return 0;
}

类模板通常需要显示实例化,不能像函数模板那样完全依赖隐式推导。

目录

  1. 前言
  2. 一、为什么需要模板?
  3. 1. 传统方法:函数重载
  4. 2. 模板方法:函数模板
  5. 二、函数模板详解
  6. 1. 概念与定义
  7. 2. 实例化机制
  8. 隐式实例化
  9. 显式实例化
  10. 3. 匹配原则
  11. 三、类模板
  12. 1. 概念与定义
  13. 2. 实例化

更多推荐文章

查看全部
  • OpenClaw 安装与飞书机器人全流程配置指南
  • 从零构建可扩展 Flutter 应用:v1.0 到 v2.0 架构演进与代码详解
  • OpenClaw 本地部署与 cpolar 外网访问配置指南
  • OpenClaw 本地部署与飞书机器人接入实战指南
  • 夸克网盘技术资源精选:书籍、软件与 AI 资料汇总
  • 2026 年机器人系统架构与核心技术路线解析
  • Ubuntu 20.04 网络配置指南
  • 基于 nanobot 构建轻量级 QQ AI 机器人及搜索模块优化实践
  • RuoYi-Vue Pro 基于 Spring Boot 与 Vue 的企业级快速开发平台
  • H.265 网页播放:WebAssembly + FFmpeg 实现浏览器端硬解软解兼容方案
  • Docker 容器内 Java 与 Python 代码运行示例
  • Spring AI 实战:基于 SSE 构建 MCP Server 与 Client
  • 10 款常用 Python 数据可视化工具推荐
  • MySQL 行级锁机制详解:Record、Gap 与 Next-Key Lock
  • CKS 核心命令速查指南
  • Ollama + Qwen 3.5 + OpenClawbot 本地部署教程
  • LIBERO 数据集:终身机器人学习基准测试平台
  • Stable Diffusion 显存优化与内存不足解决方案
  • OpenClaw 安装部署全流程:搭建自托管 AI 助手网关
  • WebODM 无人机地图制作核心优势与应用解析

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online