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

C++模板实战:函数与类模板的核心用法与细节

比较了函数重载与模板的差异后,逐步说明函数模板的定义、隐式与显式实例化原理,以及与非模板函数的匹配规则;然后通过动态栈演示类模板的实现,并指出用memcpy扩容时的类型安全注意事项。

怪力乱神发布于 2026/6/17更新于 2026/9/538 浏览
C++模板实战:函数与类模板的核心用法与细节

写 C++ 难免跟模板打交道。它是泛型编程的基石,让你写出类型无关的代码,代价为零运行时开销——因为实例化在编译期就完成了。

从重载到模板

考虑一个求最大值的函数,想支持 int、double 和 string。用函数重载的话,每个类型都要写一份类似的逻辑。

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

int max(int a, int b) {
    cout << "int version\n";
    return a > b ? a : b;
}
double max(double a, double b) {
    cout << "double version\n";
    return a > b ? a : b;
}
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 << max(3.14, 2.7) << "\n";
    cout << max("hello", "world") << "\n";
    return 0;
}

每次增加新类型就得添一个重载,改算法还得动所有版本。下面用模板来统一处理:

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

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

一份代码覆盖所有支持 > 的类型,编译器为每种实际类型生成具体的函数,效率跟手写重载一样。

函数模板

模板定义以关键词 template 开头,后面跟上模板参数列表。参数可以用 class 或 typename,习惯上多用 typename。

// 交换两个数
template<class T>
void Swap(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}

// 多参数版本
template<class T1, class T2>
void func(const T1& x, const T2& y) {
    cout << x << " " << y << endl;
}

模板本身不是函数,是编译器用来生成函数的'配方'。

隐式与显式实例化

隐式实例化就是让编译器根据实参推导类型:

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

但如果你混着传 Add(a1, d1),编译器就犯难了——没法把 int 和 double 统一成一个 T,它也不会帮你隐式转换。解决办法有两个:手动强转 Add(a1, (int)d1),或者显式指定 Add<int>(a1, d1),后者会把 d1 截成 int(可能有精度损失)。

显式实例化就是直接告诉编译器用哪个类型:

Add<int>(a1, d1);

与非模板函数的匹配

如果有个同名的普通函数,模板并不总是靠边站。规则大致是:先看看有没有完美匹配的非模板;如果没有,才考虑模板实例化。

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);     // 普通函数不匹配,模板上
}

混合使用重载和模板时,这个优先级值得记住。

类模板

类模板也不是真正的类,它描摹了一个类家族,必须绑上具体类型才变成真实的类。

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

成员函数在类外定义要带上模板前缀:

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

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

类模板必须显式实例化,不像函数模板那样可以靠参数推导。例如 Stack<int> 和 Stack<double> 是两种独立的类型。

一个自带扩容的栈

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

注意:Push 里用了 memcpy 扩容,这只对平凡类型(如 int、double)安全。如果你的栈保存的是有构造函数或虚表的对象,最好改用 std::copy 或者逐个移动构造,否则会引发未定义行为。很多教学示例会这样写,实际工程里要谨慎。

目录

  1. 从重载到模板
  2. 函数模板
  3. 隐式与显式实例化
  4. 与非模板函数的匹配
  5. 类模板
  6. 一个自带扩容的栈

更多推荐文章

查看全部
  • C++ 多态详解:从实现条件到底层原理
  • 两年 Android 开发美团面试复盘与核心技术考点解析
  • 国内主流大模型 API 调用指南与 Python 实战
  • CodeGeeX4-ALL-9B 本地部署指南:基于 Ollama 与 IDE 集成
  • RAG 技术详解:破解大模型知识、幻觉与安全困境
  • 主流 AI IDE 之一的 Qoder 和 Lingma IDE 介绍
  • Web 可访问性最佳实践:打造包容性前端界面
  • Eclipse 3.7 字体显示过小问题的修复方案
  • AI 绘画:GPU 算力租赁助力数字艺术创作
  • GitHub Copilot Pro 学生身份认证教程
  • Python 月相可视化系统:从天文计算到 Web 界面实现
  • 前端防录屏原理:EME DRM 机制解析与实战代码
  • OpenClaw 大龙虾机器人本地部署与配置实战
  • 基于 Rokid CXR-M SDK 构建 AI 远程协作助手实战
  • 二叉树层序遍历:BFS 算法可视化与实现
  • 搜狐大模型算法工程师面试题深度解析与技术复盘
  • 基于 Java 的餐馆管理系统设计与实现
  • 阿里云 RDS AI 助手上线:大模型驱动的数据库智能运维
  • Flutter 集成 BIP340 实现鸿蒙 Schnorr 签名实战
  • GOPLA框架在Stretch 3机器人上实现空间常识突破

相关免费在线工具

  • 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

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online