文章目录

  • 一、非类型模板参数
    • 应用场景
  • 二、模板的特化
    • 函数模板特化
    • 类模板特化
      • 全特化
      • 偏特化
  • 三、模板分离编译
    • 解决方法
  • 四、模板总结


一、非类型模板参数

先前介绍的函数模板和类模板都是针对类型的类模板参数,非类型模板参数有哪些使用场景呢?我们先来看下面这个例子:

const int N = 10;template <class T>
class stack
{
public:T _a[N];int top;int capacity;
};int main()
{stack<int> s1; //10stack<int> s2; //1000return 0;
}

这是我们定义的一个静态的栈,栈的大小由N来决定,我们想创建一个大小为10的栈s1和一个大小为1000的栈s2就会出现一个问题,由于静态栈类型大小的每次编译时是固定的,如果我们创建这两个栈那么空间只能开1000,那么s1就会确定浪费990大小的空间。所以这个场景下我们就可以用非类型模板参数,它就是用一个整型常量来作为类(函数)模板的一个参数,在类(函数)模板中可以把这个参数当作常量来使用。

template <class T, size_t N> //非类型模板参数
class stack
{
public:T _a[N];int top;int capacity;
};int main()
{stack<int, 10> s1; //10stack<int, 1000> s2; //10000return 0;
}

非类型模板参数也能给缺省值,模板参数和函数参数一样,缺省值只能从右往左给,传实参时只能从左往右依次给,避免参数匹配的歧义性。
若模板参数是全缺省,我们创建对象时模板参数可以都不传,但是还是要带<>:

stack<> s3;  //全缺省

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

应用场景

在这里插入图片描述

在标准库里array容器就用到了非类型模板参数,它是静态数组,类似C语言的arr[ ],。功能也和arr[ ]差不多,它创建出来后也不会对成员初始化,初始化值需要用它的一个接口fill。
array和arr[ ]的本质区别是array下标访问是用的重载函数operator[ ],所以有严格的越界读和越界写的检查,而arr[ ]本质是指针±和指针的解引用,只有在临界区域对越界写有抽查,完全没有越界读检查。

array<int, 10> a1;
a1.fill(1);  //初始化

在C++中我们用数组容器一般优先考虑vector,array相比vector的优势在开大量固定大小的数组时array效率更高,因为array是在栈上开空间,在函数栈帧创建出来时空间就开好了,vector需要在堆上动态开辟。

二、模板的特化

当我们实现出的模板函数或者模板类不符合我们预期时就可以用到模板特化,模板特化与原模板深入绑定,只有当原模版存在时模板特化才会生效。简单来说普通模板实例化是按模板生成代码,特化本身已是具体代码,它是为特殊类型改写代码,且匹配优先级比普通模板更高。

函数模板特化

函数模板特化的返回值、参数列表需与主模板替换类型后完全匹配,
我们先来看一个例子:

在这里插入图片描述

Less对传过来的整型变量可以准确的比较大小,而对于传过来的整型指针变量我们本意是想让它比较指针指向的整型变量的大小,而这里底层逻辑是比较指针变量本身的大小,所以不符合我们预期,这里就可以针对整型指针变量单开一个模板的特化版本,改写比较逻辑。

这个时候可能有的读者有和我当时一样的想法,为什么不直接写一个函数用来特殊处理int*,比如:bool Less(double* left, double* right),而是用模板特化呢?
我们要知道模板是一种泛型编程的思想,模板本意是设计出一套通用逻辑,而如果我们再创建一个普通函数,就会破坏模板本身的泛型语义一致性,使得不同类型调用Less函数时行为变得零散。模板特化就是模板为特定类型做到特殊适配,模板与特化模板是强耦合的,后续要调整通用模板的逻辑时也要考虑特化模板是否要配套修改。

我们初步设计的函数特化如下:

template<>
bool Less<int*>(int* left, int* right)
{return *left < *right;
}

注意事项
1.必须要先有一个基础的函数模板
2. 关键字template后面接一对空的尖括号<>
3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
4. 函数模板特化参数要和原模版一致,如果不同编译器可能会报一些奇怪的错误。
前三点很好理解,最后一点我们需要借助一个例子来理解,其实前面我们写的普通模板并不是最最优的,我们T类型我们无法确定,可能是内置类型也可能是string或者自定义类型,如果对象一但过大这时我们原先普通模板是传值传参因为要拷贝效率就很低了,这里的最优解是传引用,并且比较逻辑不改变参数最好还要加const修饰:

template<class T>
bool Less(const T& left, const T& right)
{return left < right;
}

而普通模板写成这样我们再写特化版本难度就变高了,在此之前我们要先明确一点,当const修饰指针变量是当const在*左边时是修饰的指针指向的内容不能修改,当const在*右边时是修饰指针变量本身不能被修改。

const T* p1;   //修饰*p1
T const * p2;  //修饰*p2
T* const p3;   //修饰p3

这里我们要写模板特化就要梳理普通模板的逻辑,因为特化版本的参数类型要和原模版参数类型严格一致,原模版是引用的T变量,并且const是修饰的被引用的T变量本身,我们要为int*写一个模板特化,那么int*在逻辑上就是原模版的T,所以我们不能写成const int*& left,因为const是修饰的int*变量指向的内容,就相当于const是修饰原模版中的*T,所以为了和原模版参数严格匹配这里模板特化的参数应该是int* const & left,这样才和原模版一样,const修饰的是引用变量本身。

template<>
bool Less<int*>(int* const & left, int* const & right)
{return *left < *right;
}

类模板特化

类模板特化就是将全部或者一部份参数确定化,模板参数全部确定化是全特化,模板参数部分确定化是偏特化,当然偏特化也可以是对参数的进一步限制。
类模板特化也需要以没有特化过的原模版为基础进行特化。模板名称和模板参数结构上需要与原模板保持一致,但在成员定义、实现逻辑上可以完全不同。
全特化的匹配优先级比偏特化优先级更高,因为全特化更现成,具体用全特化还是偏特化由使用场景决定。

全特化

全特化即是将模板参数列表中所有的参数都确定化。

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

偏特化

  • 特化部分参数
//特化部分参数
template<class T1>
class Data<T1, char>
{
public:Data() { cout << "Data<T1, char>" << endl; }
};
  • 对参数进一步限制
//对参数的进一步限制
template<class T1, class T2>
class Data<T1*, T2*>
{
public:Data() { cout << "Data<T1*, T2*>" << endl; }
};template<class T1, class T2>
class Data<T1&, T2*>
{
public:Data() { cout << "Data<T1&, T2*>" << endl; }
};

也可以两种混着特化:

template<class T2>
class Data<int, T2*>
{
public:Data() { cout << "Data<int, T2*>" << endl; }
};

三、模板分离编译

一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。也就是我们常说的把声明放在头文件,把实现放在.cpp。

模板不建议分离编译,无论的类模板还是函数模板。这是模板的定义对调用方不可见,定义方不知道调用方的使用需求导致的。
比如一个模板定义在tep.cpp,声明在tep.h,test.cpp调用这个模板。
调用方有模板实例化成什么具体类型的信息,但是它只有模板的声明,所以它在编译阶段无法实例化出具体代码,因为模板要实例化必须要模板的完整定义
定义方tep.cpp有模板的完整定义,但是它没有模板实例化成什么具体类型的信息,所以它也同样无法在编译阶段实例化出具体代码。
那么定义方模板在编译期间因为没有生成具体代码就不会进符号表,然后链接阶段调用方想找到调用模板的符号但是符号表里没有,就会因为找不到符号而报错。
归根结底就是因为.cpp文件在链接之前不能交互,那么发生在链接之前的编译阶段就会因为信息无法交互使得模板无法生成具体代码。

这也和内联函数不能分离编译的原因有有相似之处,都是因为分离编译导致
“定义不可见”,最终链接阶段符号缺失。但是内联函数是因为调用处没有内联函数的完整定义导致函数无法展开,内联函数又因为机制不会在定义处进符号表。模板是因为调用处没有模板的定义无法实例化出具体代码,定义处因为没有具体类型而无法生成具体代码。
类模板也一样,类模板是将它的成员函数分离到了两个文件。

解决方法

这个问题就是因为没有实例化造成的,所以解决这个问题就是要让模板在编译阶段能实例化,不管是定义方还是调用方。
1、模板定义的位置显式实例化。这种方法不实用,不推荐使用,因为你每调用一种类型都要显示实例化一份。所以这也验证了模板是可以分离编译的,只是不推荐。

template<class T>
void FuncT(const T& x)
{cout << "void FuncT(const T& x)" << endl;
}
//显示实例化
template
void FuncT(const int& x);

2、模板的最佳实践就是不要分离编译,把模板直接定义在头文件,调用模板的地方在编译期间就有了模板的定义生成了具体代码,填入了对应的符号,不用在链接期间再去找。

四、模板总结

优点
1、模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
2、增强了代码的灵活性
缺陷
1、模板会导致代码膨胀问题,也会导致编译时间变长(不可避免,自己写也得写)
2、出现模板编译错误时,错误信息非常凌乱,不易定位错误

以上就是小编分享的全部内容了,如果觉得不错还请留下免费的赞和收藏
如果有建议欢迎通过评论区或私信留言,感谢您的大力支持。
一键三连好运连连哦~~

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/90551.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/90551.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/90551.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

10BASE-T1S核心机制——PLCA参数详解

导语&#xff1a; PLCA是10BASE-T1S的核心机制&#xff0c;了解PLCA才能更好地使用10BASE-T1。 本文将通过介绍具体配置&#xff0c;以及实战例子&#xff0c;带你掌握PLCA。 以下测试内容使用KUNHONG-U10BT1S-EVB设备测试&#xff0c; 设备符合IEEE 802.3cg标准&#xff0…

uniapp vue apk那边输入法遮挡页面内容

解决办法&#xff1a;pages.json配置如下{"globalStyle": {"app-plus": {"softinputMode": "adjustResize"}} }效果&#xff1a; 键盘弹出时自动调整窗口大小&#xff0c;所有内容上推&#xff08;兼容性最佳&#xff09;文件内容如下…

2507C++,系统服务0与1

原文 窗口上的系统调用通过,每个由系统调用(x64)或sysenter(x86)CPU指令调用的NTDLL.dll,如NTDLL的NtCreateFile的以下输出所示: 这里 0:000> u ntdll!NtCreateFile: 00007ffcc07fcb50 4c8bd1 mov r10,rcx 00007ffcc07fcb53 b855000000 mov eax,55h…

人工智能冗余:大语言模型为何有时表现不佳(以及我们能做些什么)

像 GPT - 4 这样的大语言模型&#xff08;LLMs&#xff09;彻底改变了我们与技术交互的方式。它们可以撰写文章、生成代码、回答问题&#xff0c;甚至帮助我们构思创意。但任何花时间使用过这些模型的人都知道&#xff0c;它们的输出有时会让人感觉……不太对劲。表述冗长、格式…

Cursor替代品亚马逊出品Kiro下载

Cursor替代品亚马逊出品Kiro下载 支持Claude Sonnet4.0与3.7 点击下载 备用链接&#xff1a;https://pan.xunlei.com/s/VOW-nBmVgR3ewIIAm7jDsf99A1?pwd6bqu#

MySQL 事务管理

一、前言 CURD 不加控制&#xff0c;会有什么问题&#xff1f; CURD 满足什么属性&#xff0c;能解决上述问题&#xff1f; 买票的过程得是原子的。买票应该不能受互相的影响。买完票应该要永久有效。买前和买后都要是确定的状态。 什么是事务&#xff1f; 事务就是一组 DML 语…

yarn在macOS上的安装与镜像源配置:全方位指南

在前端开发领域&#xff0c;高效的包管理工具是提升开发效率的关键。yarn 作为一款由 Facebook 推出的包管理器&#xff0c;凭借其快速、可靠、安全的特性&#xff0c;逐渐成为众多开发者的首选。对于 macOS 用户而言&#xff0c;正确安装 yarn 并合理配置镜像源&#xff0c;能…

Qt 插件架构开发与应用

Qt的插件架构是其模块化和可扩展性的核心机制之一&#xff0c;它允许开发者通过动态加载插件&#xff08;Plugins&#xff09;扩展应用功能&#xff0c;而无需重新编译主程序。这种架构广泛应用于IDE&#xff08;如Qt Creator&#xff09;、媒体播放器&#xff08;解码器扩展&a…

打破传统局限:FinOps云成本优化助力企业云成本管理升级

在云计算日益普及的当下,企业纷纷将业务迁移到云端,以期获得更高效、灵活的IT资源管理方式。然而,云成本管理问题也随之而来,高额的云支出、资源利用不充分、成本控制难等,成为企业云管理之路上的绊脚石。此时,奇墨科技FinOps云成本优化正以其独特的优势,助力企业打破传统局限,…

HDFS写性能优化技巧详解:从理论到实践

HDFS写性能优化概述在大数据处理的生态系统中&#xff0c;Hadoop分布式文件系统&#xff08;HDFS&#xff09;作为核心存储层&#xff0c;其写性能直接影响着整个数据处理管道的效率。随着数据规模的指数级增长&#xff0c;企业对HDFS写入吞吐量和延迟的要求日益严苛&#xff0…

基于AutoJawSegment项目的CBCT图像分割实践指南

基于AutoJawSegment项目的CBCT图像分割实践指南 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家,觉得好请收藏。点击跳转到网站。 1. 项目背景与概述 1.1 CBCT在口腔医学中的应用 锥形束计算机断层扫描(Cone Beam Computed Tomograph…

docker部署的ragflow服务迁移 数据卷迁移

[docker ragflow数据迁移]目录背景一、我的配置文件✅ 数据存储路径一览&#xff08;基于你的配置文件&#xff09;关于这些 volumes 的说明&#x1f4c1; 如何查看这些卷在本地的具体位置&#xff1f;可能用到的docker 命令如下。&#x1f4e6; 总结建议&#x1f6e0;️ 如果想…

Chrome插件学习笔记(三)

Chrome插件学习笔记&#xff08;三&#xff09; 参考文章&#xff1a; https://blog.csdn.net/guoqiankunmiss/article/details/135847091https://blog.csdn.net/guoqiankunmiss/article/details/135974364 1、项目搭建 在前两篇文章中使用的原生js去操作dom&#xff0c;很费劲…

Android系统中的4KB内存页简介

deepseek回答&#xff1a; Android系统中的4KB内存页是虚拟内存管理的最小单位&#xff0c;其主要用途如下&#xff1a; 一、核心功能 虚拟地址映射 应用程序访问内存时&#xff0c;系统将虚拟地址按4KB页框映射到物理内存或磁盘空间&#xff0c;实现进程间的内存隔离和安全访…

【Chrome】下载chromedriver的地址

下载chromedriver的地址低版本的最新版本的低版本的 http://chromedriver.storage.googleapis.com/index.html 最新版本的 https://googlechromelabs.github.io/chrome-for-testing/#stable

ISP算法——从颜色恒常性到白平衡

前面文章&#xff0c;介绍了人眼感知的物体颜色取决于光源的光谱组成与物体表面的光谱反射特性之间的相互作用。人类视觉的颜色感知是生物机制与认知智能协同作用的结果&#xff0c;人眼视网膜上的视锥细胞检测光的颜色&#xff0c;视杆细胞分析光的亮度&#xff0c;再共同转化…

工业缺陷检测的计算机视觉方法总结

工业缺陷检测的计算机视觉方法总结 传统方法 特征提取方式&#xff1a; 颜色&#xff1a;基于HSV/RGB空间分析&#xff0c;如颜色直方图、颜色矩等纹理&#xff1a;采用LBP、Haar、Gabor滤波器等算子提取纹理模式形状&#xff1a;基于Hu矩、Zernike矩等数学描述符刻画几何特性尺…

js实现宫格布局图片放大交互动画

可直接运行代码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>五图交互布局</title><style>* {box-sizing: border-box;margin: 0;padding: 0;}.gallery {display: grid;grid-template-c…

easyexcel流式导出

EasyExcel 支持流式导出&#xff0c;这是它的一个重要特性。流式导出可以有效解决大数据量导出时的内存溢出问题。流式导出的优势内存友好 &#xff1a;不会一次性将所有数据加载到内存中适合大数据量 &#xff1a;可以处理百万级甚至更多的数据性能稳定 &#xff1a;内存占用相…

广州 VR 安全用电技术:工作原理、特性及优势探析​

&#xff08;一&#xff09;沉浸式学习体验​ 在广州&#xff0c;VR 用电安全培训技术给用电安全培训带来变革。借助头戴式显示设备等硬件&#xff0c;结合 3D 建模和实时渲染技术&#xff0c;打造广州特色用电场景。员工戴上 VR 设备进入虚拟电力场景&#xff0c;能看到电气设…