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

⚡ CYBER_PROFILE ⚡
/// SYSTEM READY ///


WARNING \]: DETECTING HIGH ENERGY **🌊 🌉 🌊 心手合一 · 水到渠成** ![分隔符](https://i-blog.csdnimg.cn/direct/60a3de2294e9439abad47378e657b337.gif) |------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------| | **\>\>\> ACCESS TERMINAL \<\<\<** || | [**\[ 🦾 作者主页 \]**](https://blog.csdn.net/fengtinghuqu520?spm=1000.2115.3001.5343) | [**\[ 🔥 C语言核心 \]**](https://blog.csdn.net/fengtinghuqu520/category_12955956.html) | | [**\[ 💾 编程百度 \]**](https://blog.csdn.net/fengtinghuqu520/category_13083835.html) | [**\[ 📡 代码仓库 \]**](https://blog.csdn.net/fengtinghuqu520/article/details/147275999?spm=1001.2014.3001.5502) | --------------------------------------- Running Process: 100% \| Latency: 0ms *** ** * ** *** #### 索引与导读 * [1. 非类型模板参数](#1. 非类型模板参数) * * [1.1)类型形参](#1.1)类型形参) * [1.2)非类型形参](#1.2)非类型形参) * [2. 模板的特化](#2. 模板的特化) * * [2.1)函数模板特化](#2.1)函数模板特化) * [2.2)类模板特化](#2.2)类模板特化) * * [2.2.1)全特化](#2.2.1)全特化) * [2.2.2)偏特化](#2.2.2)偏特化) * [3. 模板分离编译](#3. 模板分离编译) * [💻结尾--- 核心连接协议](#💻结尾— 核心连接协议) > [🔗Lucy的空间骇客裂缝: C++ 模板初阶](https://blog.csdn.net/fengtinghuqu520/article/details/158240822?spm=1011.2415.3001.10575&sharefrom=mp_manage_link) ## 1. 非类型模板参数 ### 1.1)类型形参 ```cpp template T add(T a, T b) { return a + b; } // 使用时: add(5, 3); // T 被替换为 int add(1.2, 3.4); // T 被替换为 double ``` *** ** * ** *** ### 1.2)非类型形参 **非类型形参不代表类型,而代表一个常量值**。你可以把它看作是在编译期确定的"常数变量" ```cpp namespace bite { // 定义一个模板类型的静态数组 // T: 类型形参,决定数组存储什么 // N: 非类型形参,决定数组的大小(编译期常量) template 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` └─────────────┘** * **`template`:** 告诉编译器,接下来的代码是一个模板。 * **`class T`:** 类型形参。代表数组要存储的数据类型(如 `int`,`double`)。 * **`size_t N = 10`:** 非类型形参。 * **`size_t`** 是无符号整型,常用于表示大小。 * **`N`** 代表数组的长度。 * **`= 10`** 是缺省值。如果用户不指定大小,默认创建长度为 `10` 的数组 > **注意:** > > 1. **浮点数、类对象以及字符串**是不允许作为非类型模板参数的 > 2. **非类型的模板参数**必须在编译期就能确认结果 *** ** * ** *** ## 2. 模板的特化 通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理,比如:实现了一个专门用来进行小于比较的函数模板 ```cpp // 函数模板 -- 参数匹配 template 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)` 时,编译器并不会直接运行这个函数 > * **类型推导:** 编译器看到`d1`和`d2`的类型是`Date` > * **生成代码:** 编译器会根据你之前定义的`template`,自动生成一份专门为 `Date` 类型服务的函数代码 > * **生成的底层伪代码:** > > ```cpp > bool Less(Date left, Date right) { > return left < right; > } > ``` > 可以看到,`Less` 绝对多数情况下都可以正常比较,但是在特殊场景下就得到错误的结果 > > 上述示例中,**`p1` 指向的 `d1` 显然小于 `p2` 指向的 `d2` 对象,但是 `Less` 内部并没有比较 `p1` 和 `p2` 指向的对象内容,而比较的是 `p1` 和 `p2` 指针的地址,这就无法达到预期而错误** 此时,就需要对模板进行特化。 即:**在原模板类的基础上,针对特殊类型所进行特殊化的实现方式** 模板特化中分为**函数模板特化** 和**类模板特化** *** ** * ** *** ### 2.1)函数模板特化 **函数模板的特化步骤:** 1. **必须要先有一个基础的函数模板** 2. **关键字 `template` 后面接一对空的尖括号 `<>`** 3. 函数名后跟一对**尖括号** ,**尖括号中指定需要特化的类型** 4. **函数形参表:** 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。 ````cpp ```cpp // 函数模板 -- 参数匹配 template bool Less(T left, T right) { return left < right; } // 对 Less 函数模板进行特化 template<> bool Less(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 class Storage { public: void print() { cout << "通用模板:存储普通数据" << endl; } }; // 2. 全特化版本:专门处理 char* 类型 template <> class Storage { public: void print() { cout << "全特化模板:专门处理字符串指针" << endl; } }; ``` *** ** * ** *** #### 2.2.2)偏特化 **偏特化(也叫部分特化)** 是指在不完全确定所有模板参数的情况下,对满足某种特定模式的参数进行定制 **注意:** 只有类模板支持偏特化,函数模板不支持偏特化(只能全特化或通过重载解决) * **偏特化通常分为两种形式:** A. 参数个数上的偏特化 当模板有多个参数时,只固定其中的一部分。 ```cpp // 基础模板 template class Data { }; // 偏特化:将第二个参数固定为 int template class Data { }; ``` B. 参数范围上的偏特化(模式匹配) 不固定具体的类型,但限制类型的**形态**(例如从 T 缩小为 T\*、T\& 或 vector) ```cpp // 基础模板 template class Handler { }; // 偏特化:只处理指针类型 template class Handler { }; // 偏特化:只处理常量引用类型 template class Handler { }; ``` *** ** * ** *** ## 3. 模板分离编译 **一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程** ------------**分离编译模式** *** ** * ** *** 假如有以下场景,模板的声明与定义分离开,在头文件中进行声明,源文件中完成定义: ```cpp // a.h template T Add(const T& left, const T& right); // a.cpp template 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` 文件合并成一个,并处理没有解决的地址问题 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/9780f372c8674aeb920e001dfb511e25.png) *** ** * ** *** **解决方法:** 1. 将声明和定义放到一个文件 `"xxx.hpp"` 里面或者 `xxx.h` 其实也是可以的。**推荐使用这种** 2. 模板定义的位置显式实例化。这种方法不实用,**不推荐使用** *** ** * ** *** ## 💻结尾--- 核心连接协议 **警告:** 🌠🌠正在接入底层技术矩阵。如果你已成功破解学习中的逻辑断层,请执行以下指令序列以同步数据:🌠🌠 *** ** * ** *** **【📡】 建立深度链接:** **关注**本终端。在赛博丛林中深耕底层架构,从原始代码到进阶协议,同步见证每一次系统升级。 **【⚡】 能量过载分发:** 执行**点赞**操作。通过高带宽分发,让优质模组在信息流中高亮显示,赋予知识跨维度的传播力。 **【💾】 离线缓存核心:** 将本页加入**收藏**。把这些高频实战逻辑存入你的离线存储器,在遭遇系统崩溃或需要离线检索时,实现瞬时读取。 **【💬】 协议加密解密:** 在**评论区**留下你的散列码。分享你曾遭遇的代码冲突或系统漏洞(那些年踩过的坑),通过交互式编译共同绕过技术陷阱。 **【🛰️】 信号频率投票:** 通过**投票**发射你的选择。你的每一次点击都在重新定义矩阵的进化方向,决定下一个被全量拆解的技术节点。 *** ** * ** *** ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/57b03915c54b43a7a03fa92dbbfe57c3.gif) ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/0905dc972de8414bb602715de3f866ee.gif)

相关推荐
yxm26336690812 小时前
洛谷P1217回文质数
java·开发语言
金斗潼关2 小时前
java反序列化入口方法介绍
java·开发语言·jvm·序列化·反序列化
雨师@2 小时前
python包uv使用介绍
开发语言·python·uv
吴声子夜歌2 小时前
JavaScript——异步编程
开发语言·前端·javascript
武藤一雄2 小时前
C# 核心技术解析:Parse vs TryParse 实战指南
开发语言·windows·microsoft·微软·c#·.netcore
一直都在5722 小时前
Java并发面经(二)
java·开发语言·spring
白藏y2 小时前
【脚手架】Protobuf基础使用
c++·protobuf
寒秋花开曾相惜2 小时前
(学习笔记)3.8 指针运算(3.8.5 变长数组)
java·c语言·开发语言·笔记·学习
南境十里·墨染春水2 小时前
C++笔记 构造函数 析构函数 及二者关系(面向对象)
开发语言·c++·笔记·ecmascript