【C++】模板进阶:深入解析模板特化

| C++语法 | 相关知识点 | 可以通过点击 | 以下链接进行学习 | 一起加油! |
|---|---|---|---|---|
| 命名空间 | 缺省参数与函数重载 | C++相关特性 | 类和对象-上篇 | 类和对象-中篇 |
| 类和对象-下篇 | 日期类 | C/C++内存管理 | 模板初阶 | String使用 |
| String模拟实现 | Vector使用及其模拟实现 | List使用及其模拟实现 | 容器适配器Stack与Queue |
本章将深入探讨模板的高级应用,重点解析模板特化的概念和实现方法。


🌈个人主页:是店小二呀
🌈C语言专栏:C语言
🌈C++专栏: C++
🌈初阶数据结构专栏: 初阶数据结构
🌈高阶数据结构专栏: 高阶数据结构
🌈Linux专栏: Linux
🌈喜欢的诗句:无人扶我青云志 我自踏雪至山巅 
文章目录
- 一、非类型模板参数
- 1.1 非类型模板参数介绍
- 1.2 非类型模板看成半成品
- 1.3 array的缺点
- 二、按需实用化
- 三、模板特化
- 3.1 模板特化概念
- 3.2 函数模板特化
- 3.3 类模板特化
- 3.3.1 全特化
- 3.4 偏特化(半特化)
- 3.4.1 特化参数部分
- 3.4.2 参数的进一步限制
- 3.5 类模板和类模板特化调用情况(注意参数对应位置)
- 四、模板分离编译
- 五、模板总结
一、非类型模板参数
1.1 非类型模板参数介绍
模板参数分类:类型形参与非类型形参
- 类型形参:出现在模板参数列表中的,跟在class或者typename之类的参数类型名称
- 非类型形参:就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当作常量来使用,以便于控制数据
具体实例:
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;};void test1(){array<int, 10> a1; //10array<int, 100> a2; //100}
}
注意事项:
- 浮点数、类对象以及字符串是不允许作为非类型模板参数的
- 非类型的模板参数必须在编译就能确认结果
1.2 非类型模板看成半成品
由于非类型的模板参数传参必须在编译就能确认结果,可以将非类型模板看作半成品,实例化结束就是成品

1.3 array的缺点
虽然array解决数组,对于越界访问的问题进行添加assert进行优化。但是array巨大的缺陷没有解决,可能会出现栈溢的情况,array为静态数组,本身是在栈上开辟空间(栈上空间比堆空间小很多)。而且使用vector不香吗?解决了越界访问和空间上的问题,而且有众多接口!
二、按需实用化
T& operator[](size_t index){assert(index < N);size(1);return _array[index];}size_t size() const { return _size; }
注意: 这里是没有发生报错的!

具体说明:
- 在operator[]函数体存在调用参数不匹配的问题,但是没有检查出来。由于编译器只是大致检查整体框架是否有错误,没有深入检查具体细节。
- 如果是调用才会实例化,就会细致检查出语法错误,从而发现错误。实例化这个类的时候,会按需实例化(调用哪个成员函数就实例化哪个,指哪打哪)。
- 根据模板实例化->半成品模板->实例化具体的类或函数->语法编译
三、模板特化
3.1 模板特化概念
通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理。比如:专门用来进行小于比较的函数模板,仿函数。
//函数模板 -- 参数匹配
template<class T>bool Less(T less, T right)
{return left + right;
}int main()
{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;
}
结果反馈:
- 这两次比较没有出现编译或运行错误,只是关于比较指针的大小结果差强人意
- 我们需要是比较指针指向的内容的大小,而不是比较指针大小或者说比较地址的大小,地址都分配是随机的,没有可比性
提出解决措施:
- 需要对模板进行特殊化,在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。这里实现个仿函数或者特化。
3.2 函数模板特化
函数模板特化的步骤
- 必须要先有一个基础的函数模板(容易被忽视)
- 关键字template后面接一对空的尖括号<>
- 函数名后跟一对尖括号,尖括号中指定需要特化的类型
- 函数形参表:必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误
具体实例:
// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{
return left < right;
}//对Less函数模板进行特化
template<>
bool Less<Date*> (Date* left, Date* right)
{return *left + *right;
}
// 调用特化之后的版本,而不走模板生成了
//有现成的,吃现成的;没有现成的,吃自己做的(模板)
cout << Less(p1, p2) << endl;
个人建议:一般情况下,不建议走函数模板。如果遇到参数类型复杂的,不好处理(比如引用)。对此可以采用函数重载,这种实现简单明了,代码的可读性搞,容易书写。(使用场景还是比较少,函数模板更加高效,但是还是不建议)
3.3 类模板特化
3.3.1 全特化
全特化即使将模板参数列表中所有的参数都确定化
template<class T1, class T2>class Data{public:Data() {cout<<"Data<T1, T2>" <<endl;}private:T1 _d1;T2 _d2;};//全特化
template<>
class Data<int, char>
{public:Data() { cout << "Data<int, char>" << endl; }
};int main()
{Data<int, int> _a1;//走模板Data<int, char> _a2;//走特化
}

3.4 偏特化(半特化)
偏特化是指任何针对模板参数进一步进行条件限制设计的特化版本**。不一定是特化部分参数,可能是对参数的进一步限制**
3.4.1 特化参数部分
将模板参数类表中一部分参数特化
template<class T1, class T2>class Data{public:Data() {cout<<"Data<T1, T2>" <<endl;}private:T1 _d1;T2 _d2;};
// 将第二个参数特化为int
template <class T1>class Data<T1, int>{public:Data() {cout<<"Data<T1, int>" <<endl;}private:T1 _d1;int _d2;};
3.4.2 参数的进一步限制
偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本
template<class T1, class T2>class Data{public:Data() {cout<<"Data<T1, T2>" <<endl;}private:T1 _d1;T2 _d2;};//两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{public:Data() { cout << "Data<T1*, T2*>" << endl; }private:T1 _d1;T2 _d2;
};//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{public:Data(const T1& d1, const T2& d2): _d1(d1), _d2(d2){cout << "Data<T1&, T2&>" << endl;}private:const T1& _d1;const T2& _d2;
};
3.5 类模板和类模板特化调用情况(注意参数对应位置)
void test ()
{Data<double, int> d1; // 调用特化的int版本Data<int, double> d2; // 调用基础的模板Data<int*, int*> d3; // 调用特化的指针版本Data<int&, int&> d4(1, 2); // 调用特化的引用版本
}

小结:
- 类模板特化实际上是为了特定类型参数组合提供了制定的实现,可以将类模板特化看作编译器等待实例化的制定实例(如果是偏特化看作是对类模板的部分定制实例)
四、模板分离编译
简单来说,就是一个项目由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式
场景分析:
//a.h
template<class T>T Add(const T& left, const T& right);int func(int x, int y);//a.cpp
template<class T>T Add(const T& left, const T& right)
{return left + right;
}int func(int x, int y)
{return x - y;
}//main.cpp
#include "a.h"
int main()
{Add(1, 2);Add(1.0, 2.0);fun(2, 2);return 0;
}

说明:
- 编译func函数都有声明,编译,检查以下函数名和参数匹配,没问题暂且可以过了,他的定义在其他.cpp文件,链接的时候再去其他文件找函数地址
- 模板在a.cpp中出现报错,通过下列两张图片,就很快发现问题了

说明:
- main.cpp调用的地方知道使用T成什么类型,但是只有函数声明,没有定义
- a.cpp 将a.h展开时,不知道这个T到底是什么类型就会报错
- 两边都没有得到需要的东西
解决办法:
- 将模板的定义和声明都放在.h文件中就不会出现链接错误
- 因为.h预处理展开后,实例化模板时,既有声明也有定义,直接实例。编译时,有函数的定义,直接就有地址,不需要链接时再去找
【分离编译扩展阅读】: http://blog.csdn.net/pongba/article/details/19130
五、模板总结
【优点】
- 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
- 增加了代码的灵活性
【缺陷】
- 模板只是把我们需要干的话,交给编译器去干了
- 模板会导致代码膨胀,也会导致编译时间变长
- 出现模板编译错误时,错误信息非常凌乱,不易定位错误
以上就是本篇文章的所有内容,在此感谢大家的观看!这里是店小二呀C++笔记,希望对你在学习C++语言旅途中有所帮助!

相关文章:
【C++】模板进阶:深入解析模板特化
C语法相关知识点可以通过点击以下链接进行学习一起加油!命名空间缺省参数与函数重载C相关特性类和对象-上篇类和对象-中篇类和对象-下篇日期类C/C内存管理模板初阶String使用String模拟实现Vector使用及其模拟实现List使用及其模拟实现容器适配器Stack与Queue 本章将…...
Python数据分析-世界上最富有的1000人
一、研究背景 随着全球化的加速发展和技术的进步,财富分配问题日益成为全球关注的焦点。财富的不平等现象日益明显,少数极富有的个人掌握了全球大部分的财富资源。了解全球最富有个人的财富分布及其背后的行业和国家因素,对于分析全球经济趋…...
CSS中隐藏滚动条的同时保留滚动功能
在CSS中,我们可以通过一些技巧来隐藏滚动条,同时保留滚动功能。以下是几种常用的方法和具体的实现步骤。 1. 使用 overflow 和 ::-webkit-scrollbar 这种方法适用于大多数现代浏览器。通过设置 overflow 属性启用滚动,同时利用 ::-webkit-s…...
我的标志:奇特的头像
<!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>与妖为邻</title><style>figu…...
中国空间计算产业链发展分析
2024中国空间计算产业链拆解 空间计算设备主要包括AR、VR、MR等终端设备。VR设备通常包括头戴式显示器(VR头盔)、手柄或追踪器等组件,用以完全封闭用户视野,营造虚拟环境体验。这些设备配备高分辨率显示屏、内置传感器和跟踪器。 …...
DAY14信息打点-JS 架构框架识别泄漏提取API 接口枚举FUZZ 爬虫插件项目
本课意义: 1.如何从表现中的JS提取价值信息 2.如何从地址中FUZZ提取未知的JS文件 3.如何从JS开放框架WebPack进行测试 一、JS 前端架构-识别&分析 在JS中寻找更多的URL地址,在JS代码逻辑(加密算法、APIKey配置、验证逻辑)中进…...
TS - tsconfig.json 和 tsconfig.node.json 的关系,如何在TS 中使用 JS 不报错
目录 1,前言2,二者关系2.1,使用 3,遇到的问题3.1,TS 中使用 JS 1,前言 通过 Vite 创建的 Vue3 TS 项目,根目录下会有 tsconfig.json 和 tsconfig.node.json 文件,并且存在引用关系…...
revisiting拉普拉斯模板
二维向量的二阶微分是Hessian矩阵,拉普拉斯算子是将两个独立的二阶微分求和,对二阶微分的近似。 我不认同冈萨雷斯的8邻域拉普拉斯模板。 MATLAB图像处理工具箱中fspecial函数’laplacian’参数给的拉普拉斯模板: 对于数字滤波器ÿ…...
深入分析计算机网络性能指标
速率带宽吞吐量时延时延带宽积往返时间RTT利用率丢包率图书推荐内容简介作者简介 速率 连接在计算机网络上的主机在数字信道上传送比特的速率,也称为比特率或数据率。 基本单位:bit/s(b/s、bps) 常用单位:kb/s&#x…...
pyflink 安装和测试
FPY Warning! 安装 apache-Flink # pip install apache-Flink -i https://pypi.tuna.tsinghua.edu.cn/simple/ Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple/ Collecting apache-FlinkDownloading https://pypi.tuna.tsinghua.edu.cn/packages/7f/a3/ad502…...
《网络故障处理案例:公司网络突然中断》
网络故障处理案例:公司网络突然中断 一、故障背景 某工作日上午,一家拥有 500 名员工的公司突然出现整个网络中断的情况。员工们无法访问互联网、内部服务器和共享文件,严重影响了工作效率。 二、故障现象 1. 所有员工的电脑…...
JavaSE:9、数组
1、一维数组 初始化 import com.test.*;public class Main {public static void main(String [] argv){int a[]{1,2};int b[]new int[]{1,0,2};// int b[]new int[3]{1,2,3}; ERROR 要么指定长度要么大括号里初始化数据算长度int[] c{1,2};int d[]new int[10];} }基本类型…...
【裸机装机系列】2.kali(ubuntu)-裸机安装kali并进行磁盘分区-2024.9最新
【前言】 2024年为什么弃用ubuntu,请参考我写的另一篇博文:为什么不用ubuntu,而选择基于debian的kali操作系统-2024.9最新 【镜像下载】 1、镜像下载地址 https://www.kali.org/get-kali/选择installer-image,进入界面下载相应的ISO文件 我…...
解决:Vue 中 debugger 不生效
目录 1,问题2,解决2.1,修改 webpack 配置2.2,修改浏览器设置 1,问题 在 Vue 项目中,可以使用 debugger 在浏览器中开启调试。但有时却不生效。 2,解决 2.1,修改 webpack 配置 通…...
Mac笔记本上查看/user/目录下的文件的几种方法
在Mac笔记本上查看/user/下的文件,可以通过多种方法实现。以下是一些常见的方法: 一、使用Finder 打开Finder:点击Dock栏中的Finder图标,或者使用快捷键Command F。 导航到用户目录: 在Finder的菜单栏中࿰…...
工程师 - ACPI和ACPICA的区别
ACPI(高级配置和电源接口)和 ACPICA(ACPI 组件架构)密切相关,但在系统电源管理和配置方面却有不同的作用。以下是它们的区别: ACPI(高级配置和电源接口) - 定义: ACPI 是…...
一文快速上手-create-vue脚手架
文章目录 初识 create-vuecreate-vue新建项目Vue.js 3 项目目录结构项目的运行和打包vite.config.js文件解析其他:webpack和Vite的区别 初识 create-vue create-vue类似于Vue CLI脚手架,可以快速创建vuejs 3项目,create-vue基于Vite。Vite支…...
笔记整理—内核!启动!—kernel部分(7)rcs文件和登录部分与密码解析
该文件的位置在/etc/init.d/rcs,前文说过这个是一个配置文件,最开始的地方首先就是PATH相关的用export导出相关的PATH做环境变量,将可执行路径导为PATH执行时就不用写全路径了,该位置的PATH路径导出了/bin、/sbin、/usr/bin、/usr…...
朴素贝叶斯 (Naive Bayes)
朴素贝叶斯 (Naive Bayes) 通俗易懂算法 朴素贝叶斯(Naive Bayes)是一种基于概率统计的分类算法。它的核心思想是通过特征的条件独立性假设来简化计算复杂度,将复杂的联合概率分布分解为特征的独立概率分布之积。 基本思想 朴素贝叶斯基于…...
高德2.0 多边形覆盖物无法选中编辑
多边形覆盖物无法选中编辑。先检查一下数据的类型得是<number[]>,里面是字符串的虽然显示没问题,但是不能选中编辑。 (在项目中排查了加载时机,事件监听…等等种种原因,就是没发现问题。突然想到可能是数据就有问题…...
突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合
强化学习(Reinforcement Learning, RL)是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程,然后使用强化学习的Actor-Critic机制(中文译作“知行互动”机制),逐步迭代求解…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
工业自动化时代的精准装配革新:迁移科技3D视觉系统如何重塑机器人定位装配
AI3D视觉的工业赋能者 迁移科技成立于2017年,作为行业领先的3D工业相机及视觉系统供应商,累计完成数亿元融资。其核心技术覆盖硬件设计、算法优化及软件集成,通过稳定、易用、高回报的AI3D视觉系统,为汽车、新能源、金属制造等行…...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...
PostgreSQL——环境搭建
一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在࿰…...
消防一体化安全管控平台:构建消防“一张图”和APP统一管理
在城市的某个角落,一场突如其来的火灾打破了平静。熊熊烈火迅速蔓延,滚滚浓烟弥漫开来,周围群众的生命财产安全受到严重威胁。就在这千钧一发之际,消防救援队伍迅速行动,而豪越科技消防一体化安全管控平台构建的消防“…...
32单片机——基本定时器
STM32F103有众多的定时器,其中包括2个基本定时器(TIM6和TIM7)、4个通用定时器(TIM2~TIM5)、2个高级控制定时器(TIM1和TIM8),这些定时器彼此完全独立,不共享任何资源 1、定…...
机器学习的数学基础:线性模型
线性模型 线性模型的基本形式为: f ( x ) ω T x b f\left(\boldsymbol{x}\right)\boldsymbol{\omega}^\text{T}\boldsymbol{x}b f(x)ωTxb 回归问题 利用最小二乘法,得到 ω \boldsymbol{\omega} ω和 b b b的参数估计$ \boldsymbol{\hat{\omega}}…...
