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

C++ 模板进阶:非类型参数、特化与分离编译

讲解 C++ 模板进阶知识,涵盖非类型模板参数、模板特化及分离编译。非类型参数支持常量但不可用浮点数或类对象。模板特化包括函数和类模板的全特化与部分特化,用于处理特殊类型逻辑。分离编译模式下,模板声明与定义需放在同一头文件中以避免实例化失败。模板虽复用代码但也导致膨胀和编译错误难定位。

机器人发布于 2026/2/24更新于 2026/10/2395 浏览
C++ 模板进阶:非类型参数、特化与分离编译

1. 非类型模板参数

模板参数分类:类型形参即出现在模板参数列表中,跟在 class 或者 typename 之后的参数类型名称。非类型形参是用一个常量作为类或函数模板的一个参数,在类或函数模板中可将该参数当成常量来使用。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
namespace bit {
    // 定义一个模板类型的静态数组
    template<class T, size_t N = 10>
    class array {
    public:
        T& operator[](size_t index) { return _array[index]; }
        const T& operator[](size_t index)const { return _array[index]; }
        size_t size()const { return _size; }
        bool empty()const { return 0 == _size; }
    private:
        T _array[N];
        size_t _size;
    };
}
int main() {
    bit::array<int, 10> a1;
    bit::array<int, 100> a2;
    bit::array<int, 100> a3;
}

1.1. 按需实例化

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
template <class T>
void Swap(T& e1, T& e2) {
    T temp = e1;
    e1 = e2;
    e2 = temp;
}
namespace bit {
    template<class T, size_t N = 10>
    class array {
    public:
        T& operator[](size_t index) { return _array[index]; }
        const T& operator[](size_t index)const { return _array[index]; }
        size_t size()const { return _size; }
        bool empty()const { return 0 == _size; }
    private:
        T _array[N];
        size_t _size;
    };
}
int main() {
    bit::array<int> a1;
    cout << a1.empty() << endl;
    cout << a1[1] << endl;
    return 0;
}

浮点数、类对象以及字符串时不能够作为非类型模板参数的。非类型的模版参数在编译期间就能确认结果。

1.2. array

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <array>
using namespace std;
int main() {
    int arr1[10];
    array<int, 10> arr2;
    // arr1[10]; // 越界写,抽查具有局限性
    // arr2[10]; // 使用 array 任何位置的越界都可以检查出来
    return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <array>
#include <vector>
using namespace std;
int main() {
    array<int, 10000000> arr1;
    cout << sizeof(arr1) << endl;
    return 0;
}

2. 模板特化

2.1. 概念

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理。比如实现了一个专门用来进行小于比较的函数模板。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Date {
public:
    Date(int year = 1900, int month = 1, int day = 1)
        : _year(year), _month(month), _day(day) {}
    bool operator<(const Date& d)const {
        return (_year < d._year) || (_year == d._year && _month < d._month) ||
               (_year == d._year && _month == d._month && _day < d._day);
    }
    bool operator>(const Date& d)const {
        return (_year > d._year) || (_year == d._year && _month > d._month) ||
               (_year == d._year && _month == d._month && _day > d._day);
    }
private:
    int _year;
    int _month;
    int _day;
};
template<class T>
bool Less(const T& element1, const T& element2) {
    return element1 < element2;
}
int main() {
    Date d1(2022, 6, 5);
    Date d2(2022, 6, 6);
    Date* p1 = &d1;
    Date* p2 = &d2;
    // Less 内部并没有比较 p1 和 p2 指向的对象内容,而比较的是指针地址
    cout << Less(p1, p2) << endl;
    return 0;
}

上述示例中,p1 指向的 d1 显然小于 p2 指向的 d2 对象,但是 Less 内部比较的是指针地址,无法达到预期。此时需要对模板进行特化。即在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化与类模板特化。

2.2. 函数模板特化

必须要有一个基础的函数模板。关键字 template 后面接一对空的尖括号。函数名后跟一对尖括号,尖括号中指定需要特化的类型。函数形参表:必须要和模板函数的基础参数类型完全相同。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Date {
public:
    Date(int year = 1900, int month = 1, int day = 1)
        : _year(year), _month(month), _day(day) {}
    bool operator<(const Date& d)const {
        return (_year < d._year) || (_year == d._year && _month < d._month) ||
               (_year == d._year && _month == d._month && _day < d._day);
    }
    bool operator>(const Date& d)const {
        return (_year > d._year) || (_year == d._year && _month > d._month) ||
               (_year == d._year && _month == d._month && _day > d._day);
    }
private:
    int _year;
    int _month;
    int _day;
};
// 基础模板
template<class T>
bool Less(T left, T right) {
    return left < right;
}
// 函数模板特化
template<>
bool Less<Date*>(Date * left, Date * right) {
    cout << "Less<Date*>(Date * left,Date * right)" << endl;
    return *left > *right;
}
int main() {
    Date d1(2022, 3, 8);
    Date d2(2023, 3, 8);
    Date* p1 = &d1;
    Date* p2 = &d2;
    cout << Less(p1, p2) << endl;
    return 0;
}

PS: 一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出。该种实现简单明了,代码的可读性高,容易书写。因为对于一些参数类型复杂的函数模板,特化时特别给出,因此函数模板不建议特化。

2.3. 类模板特化

2.3.1. 全特化
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
template <class T1, class T2>
class Date {
public:
    Date() { cout << "Date<T1,T2>" << endl; }
private:
    T1 _d1;
    T2 _d2;
};
template <>
class Date<int, char> {
public:
    Date() { cout << "Date<int,char>" << endl; }
private:
    int _d1;
    char _d2;
};
int main() {
    Date<int, int> d1;
    Date<int, char> d2;
    Date<char, int> d3;
    return 0;
}
2.3.2. 部分特化
2.3.2.1. 部分特化之参数特化
template <class T1, class T2>
class Date {
public:
    Date() { cout << "template <class T1,class T2>" << endl; }
private:
    T1 _d1;
    T2 _d2;
};
template <class T1>
class Date<T1, int> {
public:
    Date() { cout << "class Date<T1,int>" << endl; }
private:
    T1 _d1;
    int _d2;
};
int main() {
    Date<int, int> d1;
    Date<char, int> d2;
    cout << endl;
    Date<int, double> d3;
}

偏特化有以下两种表现形式:将模板参数类表中的一部分参数特化;参数更进一步的限制。偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本。

2.3.2.2. 部分特化之限制参数
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
template <class T1, class T2>
class Date {
public:
    Date() { cout << "template <class T1,class T2>" << endl; }
private:
    T1 _d1;
    T2 _d2;
};
template<class T1, class T2>
class Date<T1*, T2*> {
public:
    Date() { cout << "Date<T1 *,T2 *>" << endl; }
private:
    T1 _d1;
    T2 _d2;
};
template<class T1, class T2>
class Date<T1&, T2&> {
public:
    Date() { cout << "class Date<T1&, T2&>" << endl; }
private:
    T1 _d1;
    T2 _d2;
};
int main() {
    // 走基础模板
    Date<int, int> d1;
    // 走指针特化模板
    Date<int*, int*> d2;
    cout << endl;
    // 走引用特化模板
    Date<int&, int&> d3;
    return 0;
}

3. 模板的分离编译

3.1. 什么是分离编译

一个程序由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。

3.2. 模板的分离编译

假如有以下场景,模板的声明与定义分离开,在头文件中进行声明,源文件中完成定义。

3.2.1. Separate_Compilation_of_Templates.h
#pragma once
#include <iostream>
using namespace std;
template <class T>
T Add(const T& left, const T& right);
3.2.2. Separate_Compilation_of_Templates.cpp
#define _CRT_SECURE_NO_WARNINGS
#include "Separate_Compilation_of_Templates.h"
template <class T>
T Add(const T& left, const T& right) {
    return left + right;
}
3.2.3. Test.cpp
#define _CRT_SECURE_NO_WARNINGS
#include "Separate_Compilation_of_Templates.h"
int main() {
    Add(1, 2);
    Add(1.0, 2.0);
    return 0;
}

3.3. 解决办法

将声明和定义放到一个文件 xxx.hpp 里面或者 xxx.h 其实也是可以的。推荐使用这种。模板定义的位置显式实例化。这种方法不实用,不推荐使用。

3.3.1. Separate_Compilation_of_Templates.h
#pragma once
#include <iostream>
using namespace std;
#include <stdio.h>
template <class T>
T Add(const T& left, const T& right) {
    return left + right;
}
3.3.2. Test.cpp
#define _CRT_SECURE_NO_WARNINGS
#include "Separate_Compilation_of_Templates.h"
int main() {
    printf("%d\n", Add(1, 2));
    printf("%lf\n", Add(1.0, 2.0));
    return 0;
}

4. 模板总结

优点:模板复用了代码,节省资源,更快的迭代开发,C++ 的标准模板库 (STL) 因此而产生。增强了代码的灵活性。 缺陷:模板会导致代码膨胀问题,也会导致编译时间变长。出现模板编译错误时,错误信息会十分凌乱,不容易定位错误。

目录

  1. 1. 非类型模板参数
  2. 1.1. 按需实例化
  3. 1.2. array
  4. 2. 模板特化
  5. 2.1. 概念
  6. 2.2. 函数模板特化
  7. 2.3. 类模板特化
  8. 2.3.1. 全特化
  9. 2.3.2. 部分特化
  10. 2.3.2.1. 部分特化之参数特化
  11. 2.3.2.2. 部分特化之限制参数
  12. 3. 模板的分离编译
  13. 3.1. 什么是分离编译
  14. 3.2. 模板的分离编译
  15. 3.2.1. SeparateCompilationof_Templates.h
  16. 3.2.2. SeparateCompilationof_Templates.cpp
  17. 3.2.3. Test.cpp
  18. 3.3. 解决办法
  19. 3.3.1. SeparateCompilationof_Templates.h
  20. 3.3.2. Test.cpp
  21. 4. 模板总结

更多推荐文章

查看全部
  • 鸿蒙金融理财应用:生态合作与用户运营优化实践
  • AI 生成代码时代,人类开发者如何保持创意价值?
  • Linux 核心 IO 模型深析:Cmake 构建与 poll 多路转接实现
  • 2024 AI 大模型面试核心知识点与实战技巧
  • AI 产品经理必备技术能力与学习路径指南
  • 栈的压入弹出序列判断算法详解
  • Ubuntu 24.04 安装向日葵 SunloginClient 依赖缺失修复方案
  • Python 批量给图片添加文字或图片水印实战
  • 模拟算法专题:替换问号、提莫攻击等 5 题详解
  • 代码从本地 Push 到服务上线的全流程解析
  • Qwen-Image-Lightning 水墨画生成实战指南
  • 深入理解 AVL 树:平衡因子与自旋调整
  • 前端渲染 Markdown 格式:主流库选型与实战
  • Linux 本地部署 ESPHome 及外网访问方案
  • Llama Guard 3 8B 模型安全分类技术报告解读
  • Web 应用全栈开发实践:从前端到后端
  • C++ 进阶:从裸指针到智能指针的内存管理进化
  • 国产 AI Agent 工具全对比:八大模型部署与使用指南
  • Android 插件化技术:动态加载综述与核心原理
  • 腾讯开源 HunyuanVideo 1.5:8.3B 参数模型支持消费级显卡部署

相关免费在线工具

  • 加密/解密文本

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