【C++模板进阶】——【非类型模板参数 / 模板的特化 / 模板分离编译】

⚡ CYBER_PROFILE ⚡
/// SYSTEM READY ///


WARNING : DETECTING HIGH ENERGY

🌊 🌉 🌊 心手合一 · 水到渠成

|------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------|
| >>> ACCESS TERMINAL <<< ||
| 🦾 作者主页 | 🔥 C语言核心 |
| 💾 编程百度 | 📡 代码仓库 |


Running Process: 100% | Latency: 0ms


索引与导读

🔗Lucy的空间骇客裂缝: C++ 模板初阶

1. 非类型模板参数

1.1)类型形参

cpp 复制代码
template <typename T> 
T add(T a, T b) {
    return a + b;
}

// 使用时:
add<int>(5, 3);       // T 被替换为 int
add<double>(1.2, 3.4); // T 被替换为 double

1.2)非类型形参

非类型形参不代表类型,而代表一个常量值。你可以把它看作是在编译期确定的"常数变量"

cpp 复制代码
namespace bite
{
    // 定义一个模板类型的静态数组
    // T: 类型形参,决定数组存储什么
    // N: 非类型形参,决定数组的大小(编译期常量)
    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];        // 利用非类型形参 N 开辟空间
        size_t _size = N;   // 初始化大小为 N
    };
}

╔═█▓▒░ CODE CORE 🔥
┌─────────────┐
│ 代码关键点 │namespace bite
└─────────────┘

  • 作用: 定义一个名为 bite 的命名空间。

  • 目的: 防止类名 array 与标准库中的 std::array 产生冲突(即"命名污染")。

╔═█▓▒░ CODE CORE 🔥
┌─────────────┐
│ 代码关键点 │template<class T, size_t N = 10>
└─────────────┘

  • template 告诉编译器,接下来的代码是一个模板。

  • class T 类型形参。代表数组要存储的数据类型(如 intdouble)。

  • size_t N = 10 非类型形参。

    • size_t 是无符号整型,常用于表示大小。
    • N 代表数组的长度。
    • = 10 是缺省值。如果用户不指定大小,默认创建长度为 10 的数组

注意:

  1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的
  2. 非类型的模板参数必须在编译期就能确认结果

2. 模板的特化

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

cpp 复制代码
// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{
    return left < right;
}

int main()
{
    cout << Less(1, 2) << endl; // 可以比较,结果正确

    Date d1(2022, 7, 7);
    Date d2(2022, 7, 8);
    cout << Less(d1, d2) << endl; // 可以比较,结果正确

    Date* p1 = &d1;
    Date* p2 = &d2;
    cout << Less(p1, p2) << endl; // 可以比较,结果错误

    return 0;
}

详细代码解析

  • 当你写下 Less(d1, d2) 时,编译器并不会直接运行这个函数
    • 类型推导: 编译器看到d1d2的类型是Date
    • 生成代码: 编译器会根据你之前定义的template<class T>,自动生成一份专门为 Date 类型服务的函数代码
    • 生成的底层伪代码:
cpp 复制代码
bool Less(Date left, Date right) {
   return left < right; 
}

可以看到,Less 绝对多数情况下都可以正常比较,但是在特殊场景下就得到错误的结果

上述示例中,p1 指向的 d1 显然小于 p2 指向的 d2 对象,但是 Less 内部并没有比较 p1p2 指向的对象内容,而比较的是 p1p2 指针的地址,这就无法达到预期而错误

此时,就需要对模板进行特化。
即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方式

模板特化中分为函数模板特化类模板特化


2.1)函数模板特化

函数模板的特化步骤:

  1. 必须要先有一个基础的函数模板
  2. 关键字 template 后面接一对空的尖括号 <>
  3. 函数名后跟一对尖括号尖括号中指定需要特化的类型
  4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
cpp 复制代码
```cpp
// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{
    return left < right;
}

// 对 Less 函数模板进行特化
template<>
bool Less<Date*>(Date* left, Date* right)
{
    return *left < *right;
}

int main()
{
    cout << Less(1, 2) << endl;

    Date d1(2022, 7, 7);
    Date d2(2022, 7, 8);
    cout << Less(d1, d2) << endl;

    Date* p1 = &d1;
    Date* p2 = &d2;
    cout << Less(p1, p2) << endl; // 调用特化之后的版本,而不走模板生成了
    return 0;
}

注意: 一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出。

cpp 复制代码
bool Less(Date* left, Date* right)
{
    return *left < *right;
}

该种实现简单明了,代码的可读性高,容易书写,因为对于一些参数类型复杂的函数模板,特化时特别给出,因此函数模板不建议特化


2.2)类模板特化

2.2.1)全特化

全特化是指为模板参数提供一组具体的类型。一旦进行了全特化,原来的"模板"就不再具有通用性,而是变成了一个针对特定类型的"具体类"


语法特点

  • 必须先声明基础模板。
  • 使用 template <> 开头(不带任何参数)。
  • 在类名后的角括号中指定具体的类型
cpp 复制代码
// 1. 基础模板 (Primary Template)
template <typename T>
class Storage {
public:
    void print() { cout << "通用模板:存储普通数据" << endl; }
};

// 2. 全特化版本:专门处理 char* 类型
template <>
class Storage<char*> {
public:
    void print() { cout << "全特化模板:专门处理字符串指针" << endl; }
};

2.2.2)偏特化

偏特化(也叫部分特化) 是指在不完全确定所有模板参数的情况下,对满足某种特定模式的参数进行定制

注意: 只有类模板支持偏特化,函数模板不支持偏特化(只能全特化或通过重载解决)

  • 偏特化通常分为两种形式:

A. 参数个数上的偏特化

当模板有多个参数时,只固定其中的一部分。

cpp 复制代码
// 基础模板
template <typename T1, typename T2>
class Data { };

// 偏特化:将第二个参数固定为 int
template <typename T1>
class Data<T1, int> { };

B. 参数范围上的偏特化(模式匹配)

不固定具体的类型,但限制类型的形态(例如从 T 缩小为 T*、T& 或 vector)

cpp 复制代码
// 基础模板
template <typename T>
class Handler { };

// 偏特化:只处理指针类型
template <typename T>
class Handler<T*> { };

// 偏特化:只处理常量引用类型
template <typename T>
class Handler<const T&> { };

3. 模板分离编译

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


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

cpp 复制代码
// a.h
template<class T>
T Add(const T& left, const T& right);

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

// main.cpp
#include "a.h"
int main()
{
    Add(1, 2);
    Add(1.0, 2.0);

    return 0;
}

代码分析:
C/C++ 程序要运行,一般要经历以下步骤:
预处理编译汇编链接

  • 编译:对程序按照语言特性进行词法、语法、语义分析,错误检查无误后生成汇编代码。

注意: 头文件不参与编译,编译器对工程中的多个源文件是分离开单独编译的

  • 链接 :将多个 .obj 文件合并成一个,并处理没有解决的地址问题

解决方法:

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

💻结尾--- 核心连接协议

警告: 🌠🌠正在接入底层技术矩阵。如果你已成功破解学习中的逻辑断层,请执行以下指令序列以同步数据:🌠🌠


【📡】 建立深度链接: 关注本终端。在赛博丛林中深耕底层架构,从原始代码到进阶协议,同步见证每一次系统升级。

【⚡】 能量过载分发: 执行点赞操作。通过高带宽分发,让优质模组在信息流中高亮显示,赋予知识跨维度的传播力。

【💾】 离线缓存核心: 将本页加入收藏。把这些高频实战逻辑存入你的离线存储器,在遭遇系统崩溃或需要离线检索时,实现瞬时读取。

【💬】 协议加密解密:评论区留下你的散列码。分享你曾遭遇的代码冲突或系统漏洞(那些年踩过的坑),通过交互式编译共同绕过技术陷阱。

【🛰️】 信号频率投票: 通过投票发射你的选择。你的每一次点击都在重新定义矩阵的进化方向,决定下一个被全量拆解的技术节点。



相关推荐
MC皮蛋侠客6 小时前
Google Test 单元测试指南
c++·单元测试·google test
方也_arkling7 小时前
【Java-Day08】static / final / 枚举
java·开发语言
艾莉丝努力练剑7 小时前
【Linux:文件】Ext系列文件系统进阶
linux·运维·服务器·c++·文件系统·文件io·ext
风吹夏回7 小时前
Python 全局异常处理:从“满屏 try-except”到优雅兜底
开发语言·python
Chengbei117 小时前
一站式源码安全检测工具、云安全 / APP / 小程序源码敏感信息递归多层目录扫描AK、JWT、手机号、身份证等敏感信息
java·开发语言·安全·web安全·网络安全·系统安全·安全架构
llz_1127 小时前
web-第一次课后作业
java·开发语言·idea
小熊Coding8 小时前
Python爬取当当网二手图书项目实战!
开发语言·爬虫·python·beautifulsoup·requests·二手图书
秋98 小时前
Java项目运行5天左右自动宕机:系统性定位与解决方案
java·开发语言·python
xiaoshuaishuai88 小时前
C# 内存管理与资源泄漏
开发语言·c#
lsx2024069 小时前
SVN 检出操作
开发语言