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

C++ 模板初阶:函数与类模板详解

C++ 模板机制通过编译期实例化实现泛型编程,核心在于将类型视为参数。文章对比了函数重载与模板的差异,展示了函数模板的定义、隐式与显式实例化过程及类型推导规则。同时讲解了类模板的语法结构,特别是成员函数在类外定义时的作用域限定问题。通过 Swap、Max 及动态栈等实战案例,说明了模板如何减少重复代码并提升复用性,强调了编译器在模板匹配中的优先级策略。

怪力乱神发布于 2026/3/25更新于 2026/7/2331 浏览
C++ 模板初阶:函数与类模板详解

C++ 模板初阶:函数与类模板详解

C++ 模板是实现泛型编程的核心工具,允许我们编写与类型无关的代码。它在编译时进行实例化,根据实际使用的类型生成具体代码,因此不会产生额外的运行时开销。

为什么需要模板?

在编写函数或类时,如果希望它们能处理多种数据类型(如 int、double、string),传统方法是使用函数重载。但这样会产生大量重复代码,或者失去类型信息。

场景: 需要编写一个求两个数最大值的函数,支持 int、double 和 string(按字典序)。

传统方法:函数重载
#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";      
    cout << (, ) << ; 
    cout << (, ) << ; 
     ;
}
// int version
max
3.14
2.7
"\n"
// double version
max
"hello"
"world"
"\n"
// string version
return
0

这种方式的问题很明显:每个类型都要写一遍几乎相同的函数体,扩展性差。如果要支持新类型(如 char),必须添加新重载,修改算法逻辑时还得改动所有重载函数。

模板方法:函数模板

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

#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;
}

优点显而易见:一份代码适用于所有类型,只要该类型支持 operator>。维护成本低,且编译器生成的代码效率与手写重载相同。

函数模板详解

概念与定义

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

语法提示:

  • 使用 template <class T> 或 template <typename T>。
  • T 是占位符,可自定义名称。
  • 注意区分 class 和 struct,模板参数中通常用 class 或 typename。
// 示例 1:交换两个数
template<class T>
void Swap(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}

// 示例 2:返回类型也是模板参数
template<class T>
T max(T a, T b) {
    return a > b ? a : b;
}

// 示例 3:多个模板参数
template<class T1, class T2>
void func(const T1& x, const T2& y) {
    cout << x << " " << y << endl;
}
实例化原理

函数模板本身不是函数,而是编译器用来产生特定类型函数的模具。这个过程叫实例化,分为隐式和显式。

1. 隐式实例化 让编译器根据实参推演模板参数的实际类型。

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;
}

注意: 如果只有一个模板参数,传入两个不同类型,编译器无法确定 T 是哪一个,会报错。例如 Add(a1, d1) 会导致编译失败。编译器在模板中通常不会进行隐式类型转换,因为这可能导致歧义。

解决办法:

  • 用户强制转换:Add(a1, (int)d1)。
  • 显式指定类型:Add<int>(a1, d1)。这会告诉编译器跳过推导,直接使用 int 实例化。此时编译器会检查 d1 是否能转换为 int(可以,会有截断)。

2. 显式实例化 在函数名后的 <> 中直接指定类型。

int main() {
    int a = 10;
    double b = 20.0;
    Add<int>(a, b); // 显式指定 T 为 int
    return 0;
}
匹配原则

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

  1. 优先调用非模板函数(如果完全匹配)。
  2. 如果模板能产生更好的匹配(如类型更精确),则选择模板。
// 专门处理 int 的加法
int Add(int left, int right) { return left + right; }
// 通用加法模板
template<class T1, class T2> T1 Add(T1 left, T2 right) { return left + right; }

void Test() {
    Add(1, 2);       // 匹配非模板函数
    Add(1, 2.0);     // 匹配模板函数(因为 1 是 int,2.0 是 double,非模板不匹配)
}

类模板

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

定义与实例化

类模板定义必须以 template 开头。类名不能代表类型,必须绑定模板参数才能成为类型。

template <typename 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--];
}
实战示例:动态栈实现

这里演示一个带扩容功能的栈类模板。

template<class T>
class Stack {
public:
    Stack(int n = 4) : _array(new T[n]), _capacity(n), _size(0) {}
    ~Stack() { delete[] _array; }
    
    void 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;
    }

private:
    T* _array;
    size_t _capacity;
    size_t _size;
};

int main() {
    Stack<int> st1;
    st1.Push(1);
    st1.Push(2);
    
    Stack<double> st2;
    st2.Push(1.1);
    st2.Push(1.2);
    
    return 0;
}

类模板都需要显示实例化,不能像函数模板那样通过参数隐式推导。比如 Stack<int> st1; 就是明确的实例化操作。

目录

  1. C++ 模板初阶:函数与类模板详解
  2. 为什么需要模板?
  3. 传统方法:函数重载
  4. 模板方法:函数模板
  5. 函数模板详解
  6. 概念与定义
  7. 实例化原理
  8. 匹配原则
  9. 类模板
  10. 定义与实例化
  11. 实战示例:动态栈实现
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 零基础 AI 入门指南:从环境搭建到代码调用
  • 基于 ESP32-S3 芯片的 Wi-Fi 智能机器人设计与实现
  • OpenClaw Web Search 配置与渠道选择指南
  • 智能家居中控屏:基于 GLM-4.6V-Flash-WEB 识别家庭成员与习惯
  • GESP 2025 年 12 月 C++ 六级真题解析(单选 8-15 题)
  • C++11 右值引用、移动语义及 Lambda 表达式详解
  • 智能客服对话机器人设计:从架构到生产部署
  • VS Code 中 GitHub Copilot 核心功能与实战入门
  • 使用 Docker 部署 OpenClaw 实践
  • C++ 笔试刷题:排序子序列、消减整数与最长上升子序列
  • MVEL 表达式编译与执行测试
  • CentOS Firewalld 服务、端口及访问控制实战指南
  • Git 与 TortoiseGit 小乌龟安装配置指南
  • Nano Banana AI 绘图中文模糊修复:Seedream 4.5 重渲染方案
  • N46Whisper:基于 Whisper 的日语语音转字幕工具
  • Spring Boot Web 三大核心交互实战:表单、AJAX 与 JSON
  • OpenClaw 本地 AI 助手部署与飞书对接指南
  • Retinaface+CurricularFace 人脸识别服务 Kubernetes StatefulSet 部署实战
  • FPGA 比特流深度解析
  • Qclaw 微信 AI 智能体工具三步上手指南

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如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