template——模板进阶(C++)

        在之前的文章中,介绍了模板初阶:Cpp_桀桀桀桀桀桀的博客-CSDN博客

        在本篇中将会对模板进一步的讲解。本篇中的主要内容为:非类型模板参数、函数模板的特化、类模板的特化(其中包含全特化和偏特化),最后讲解了模板的分离编译问题,以及出现链接错误的原因。

        目录如下:

目录

1. 非类型模板参数

1.1 模板的按需实例化

2. 模板的特化

2.1 特化的概念

2.2 函数模板特化

2.3 类模板特化

3. 模板的分离编译

3.1 模板的分离编译及其原理

1. 非类型模板参数

        模板参数分为:类类型形参非类型形参

        类型形参:出现在模板参数列表中,跟在 class 或者 typename 之类的参数类型名称之后。

        非类型形参:使用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。

        如下所示的两份代码:

namespace MyArray1
{
// 使用宏定义
#define N 10
	// 定义一个模板类型的静态数组
	template<class T>
	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;
	};
}

namespace MyArray2
{
	// 使用宏定义
	// 定义一个模板类型的静态数组
	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;
	};
}

        如上所示,当我们想要写一个常量数组类,我们可以使用两种方法,一种是使用宏定义来确定数组的大小,另一种是使用非类型模板参数来充当数组的大小,我们在使用非类型模板参数的时候,也可以像使用函数参数一样,给一个缺省值。

        关于使用其他的非类型模板参数,我们的非类型模板参数只能使用整型做模板参数,对于其他非类型的模板参数,只有到 C++20 标准之后才可以使用。如下:

        关于类(函数)模板传参与函数传参的时刻:对于类模板传参而言,传参的时候是在编译阶段,因为模板属于一个半成品,在编译阶段需要使用传入的参数来生成确定的类(代码);而对于函数传参而言,函数传参是在运行时传参,将我们需要运行的参数传入函数中进行计算。

1.1 模板的按需实例化

        在模板实例化的时候,并不会检测语法错误,如下:

        我们在函数中调用 assert 函数和 size 函数都出现了语法错误,但是我们在生成解决方案的时候却可以通过,这是因为对于模板而言,这是一个半成品,在语法编译的的时候,因为并没有调用类函数,所以并不会检测语法。

        当我们实例化一个对象的时候,是否会检测出错误呢?如下:

        当我们实例化一个对象的时候,调用其中一个函数的时候,也还是不会检测出错误,这是因为按需实例化,不仅仅是类的按需实例化,类函数也是按需实例化,只有当调用的函数出现错误的时候,才会检测出来

        对于模板实例化的步骤为:根据模板实例化 --> 半成品模板 --> 实例化成具体的类/函数 --> 语法编译。

2. 模板的特化

2.1 特化的概念

        通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要我们进行特殊的处理,如下:

        如上所示,当使用指针进行比较的时候,原本应该输出为0,但是却输出为1,这是因为在 Less 中并没有比较指针指向的内容重载函数,而是直接的比较指针的大小,所以输出的结果显示错误。

        这个时候,我们就需要对模板进行特化,即:在原模板的基础上,针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化类模板特化

2.2 函数模板特化

        函数模板特化的步骤

        1. 必须要现有一个基础的函数模板;

        2. 关键字 template 后面接一个空的<>;

        3. 函数名后面跟一对尖括号,尖括号内指定需要特化的类型;

        4. 函数形参必须要和模板函数的基础参数类型完全相同,如果不同,编译器可能会薄一些奇怪的错误。

        如下:

template<class T>
bool Less(T x, T y) {
	return x < y;
}

// 函数模板特化
template<>
bool Less<Date*>(Date* x, Date* y) {
	return *x < *y;
}

// 函数重载
bool Less(Date* x, Date* y) {
	return *x < *y;
}

        如上所示的函数模板特化形式,就可以解决这样的问题,但是其实我们也可以写一个重载函数来解决这个问题。通常情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现功能,最好直接写一个重载函数,通常不建议写函数模板特化。

2.3 类模板特化

        关于类模板的特化其中包含全特化偏(半)特化

        其中全特化为将模板参数列表中所有的参数都确定化,如下:

template<class T1, class T2>
class A {
public:
	A() { cout << "A<T1, T2>" << endl; }
private:
	T1 _a1;
	T2 _a2;
};

// 将参数列表中所有的参数都确定化
template<>
class A<int, char> {
public:
	A() { cout << "A<int, char>" << endl; }
private:
	int _a1;
	char _a2;
};

void Test01() {
	A<int, int> aa1;
	A<int, char> aa2;
}

        关于偏特化,也分为两种类型,一种是将模板参数类表中的一部分参数特化,另一种是将参数进一步的限制,如下:

// 将模板参数表中的一部分参数特化
template<class T1>
class A<T1, int> {
public:
	A() { cout << "A<T1, int>" << endl; }
private:
	T1 _a1;
	int _a2;
};

// 将模板参数表中的参数进一步的限制
template<class T1,class T2>
class A<T1*, T2*> {
public:
	A() { cout << "A<T1*, T2*>" << endl; }
private:
	T1 _pa1;
	T2 _pa2;
};

3. 模板的分离编译

        首先关于什么是分离编译,分离编译就是将一个类或者一个函数的声明与定义分别放在不同的文件之中,然后在生成目标文件的过程中,需要将所有目标文件(每一个源文件都会生成一个目标文件)链接起来,形成一个单一的可执行文件的过程叫做分离编译模式

3.1 模板的分离编译及其原理

        关于模板的编译与分离,就是将一个模板的声明放在一个 .h 文件中,然后将一个模板的定义放在另一个 .cpp 文件之中,如下:

        如上图所示,当我们将模板的声明与定义分隔开的时候,调用对应函数的时候就会导致报错(若我们不调用对应的函数的时候,就不会报错,这是因为对于模板而言,只是一个半成品,只有按需实例化的时候才会检测出错误),链接错误。

        出现这种错误的原因:

        当我们声明和定义分离的时候,我们将头文件包含在当前 main 函数所在的文件中,在预处理阶段,会将头文件在 main 函数所在的文件展开,然后在编译阶段,我们将我们的模板进行实例化,根据传入的模板参数进行实例化,有多少种就会实例化多少种代码,然后在链接的阶段,会去找我们调用的函数,但是在展开实例化的函数中,我们只有声明,然后就会去分离定义的文件中寻找,当在其他文件中找到定义的时候,这里的定义并没有在编译阶段跟着实例化,仍然还是带有模板参数的模板函数,所以链接的时候就找不对对应需要的实例化模板函数

        关于以上的解决方法,在分离定义中进行显示实例化,如下:

        如上所示,我们可以使用显示实例化来解决这个问题,但是这种问题的解决方法也仅仅只是治标不治本,当我们传入另一个模板实参的时候,还需要显示实例化一次,这并符合模板的特点,所以说,最好将模板的定义和声明放在同一个地方,或者分离定义在同一个文件中。

        另外,关于模板这一块的调用形式很奇怪,一不小心就会用错。所以对于模板的使用,建议将模板的声明和定义放入到同一个文件中(因为在同一个文件中时,既有声明也有定义,直接就实例化,编译的时候,有函数定义,就有函数地址,就不需要等到链接的时候在去寻找

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【计算机网络篇】数据链路层(9)使用集线器的共享式以太网

文章目录 &#x1f6f8;使用同轴电缆的共享总线以太网 &#x1f386;使用集线器的共享式以太网&#x1f95a;集线器的特点 &#x1f354;10BASE-T星型以太网 &#x1f6f8;使用同轴电缆的共享总线以太网 若总线上的某个机械连接点接触不良或断开&#xff0c;则整个网络通信就不…

【前端工程化指南】Git常见操作之忽略文件

默认情况下&#xff0c;Git管理代码版本时会对所有文件进行跟踪&#xff0c;但有些时候我们并不希望项目中的一些文件上传到远程仓库或公共仓库中&#xff0c;例如密钥&#xff0c;个人隐私文件等。因此Git提供了两种忽略跟踪文件的方式.gitignore文本文件与git rm命令&#xf…

亿级流量下通用的高并发架构设计

既然是亿级用户应用&#xff0c;那么高并发必然是其架构设计的核心要素。 本文我们将介绍高并发架构设计的一些通用设计方案。 关键词&#xff1a;读/写分离、数据缓存、缓存更新、CQRS、数据分片、异步写 本文节选自电子工业出版社博文视点刚刚出版的《亿级流量系统架构设计…

Java随笔1

1.编程中组件的概念&#xff1a; 在编程中&#xff0c;组件&#xff08;Component&#xff09;通常指的是一种可重用的、模块化的代码单元&#xff0c;它封装了特定的功能或用户界面元素&#xff0c;并提供了与其他代码进行交互的接口。组件可以看作是对数据和方法的简单封装&…

ADS基础操作篇2

上篇文章《ADS基础介绍篇1》,对ADS界面,常用小工具及自带设计模板进行了介绍。ADS使用非常方便,含大量的控件和仿真模板。这篇文章我们主要讲解ADS的基础操作,包含Workspace、原理图、symbol的创建,仿真结果查看及优化。 1. 新建Workspace 添加名称及路径后,点击create…

共享充电宝语音芯片ic方案支持远程4g无线更新语音

一、简介 共享充电宝语音芯片ic方案支持远程4g无线wifi蓝牙更新语音 共享充电宝已经是遍布在大街小巷的好产品&#xff0c;解决了携带充电宝麻烦的痛点 但是很多的共享充电宝在人机交互方便&#xff0c;还做得不够好&#xff0c;比如&#xff1a;借、还设备没有语音提示&…

基于SSM的计算机课程实验管理系统的设计与实现(源码)

| 博主介绍&#xff1a;✌程序员徐师兄、8年大厂程序员经历。全网粉丝15w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f44…

AI视频教程下载:用ChatGPT制作 YouTube视频的指南

课程大纲&#xff1a; 面向 YouTuber 的 ChatGPT YouTube关键词研究 YouTube标题 YouTube缩略图 YouTube社区帖子 组织您的 YouTube 视频 本课程将通过两个不同领域的YouTube视频&#xff0c;展示如何使用Chat GPT来创建关键词、标题、缩略图、描述和社区帖子。 关键词研…

【Linux网络】Https【下】{CA认证/证书的签发与认证/安全性/总结}

文章目录 1.引入证书【为方案五铺垫】1.1再谈https1.2SSL/TLS1.3CA机构1.4理解数字签名1.4继续铺垫1.5方案五服务端申请证书回顾一二三回顾方案四方案五过程寻找方案五的漏洞客⼾端对证书进⾏认证 2.查看证书2.1查看浏览器的受信任证书发布机构2.2中间⼈有没有可能篡改该证书2.…

Postman工具介绍与安装

一、Postman介绍 Postman 乃是一款对 HTTP 协议予以支持的接口调试及测试工具&#xff0c;其突出特性在于功能强大&#xff0c;并且使用简便、易用性良好。不管是开发人员开展接口调试工作&#xff0c;还是测试人员进行接口测试任务&#xff0c;Postman 均属于首选工具之一。 接…

会声会影2024中文旗舰免费版(Corel VideoStudio)下载安装包附带会声会影软件注册机

一、软件背景及版本概述 会声会影&#xff08;Corel VideoStudio&#xff09;是由加拿大Corel公司发布的一款视频编辑软件&#xff0c;该软件以其功能丰富、操作简便而广受好评。2024年版本在继承之前版本优点的基础上&#xff0c;进行了诸多创新和改进&#xff0c;为用户提供…

2万字干货:如何从0到1搭建一套会员体系(4)

开始本节前还是一样来个灵魂发问&#xff1a;为什么产品需要用户标签&#xff0c;或者用户标签有什么意义/价值&#xff1f; 某些业务场景下使用会员等级无法满足业务需要。比如新用户激活、老用户福利以及沉默客户唤醒等等。 用户等级划分的逻辑和维度有些局限性&#xff0c;…

java项目之共享汽车管理系统(springboot+mysql+vue)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的共享汽车管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 共享汽车管理系统的主要…

什么是数据恢复软件?数据恢复软件怎么下载使用?

“我一直在寻找一款出色的 PC Android数据恢复软件&#xff0c;我可以下载。有很多&#xff0c;但大多数都需要我付钱。你能推荐一个我可以免费下载的好书吗&#xff1f; 奇客数据恢复安卓版是恢复已删除或丢失的Android数据的最安全工具。免费下载奇客数据恢复安卓版下面尝试所…

一分钟带你了解什么是等保测评

等保测评&#xff0c;即网络安全等级保护测评&#xff0c;是依据国家信息安全等级保护制度规定&#xff0c;对信息系统进行安全技术测评和安全管理测评&#xff0c;以确定系统的安全保护水平是否达到预定的安全等级要求。以下是等保测评的相关知识点总结&#xff1a; 测评概述&…

Google: 在新知识上微调大语言模型是否会鼓励产生幻觉?

摘要 当大型语言模型通过监督式微调进行对齐时,它们可能会遇到在预训练期间没有获得的新事实信息。人们经常推测,这可能会教导模型产生事实上不正确的回应的行为,因为模型被训练成生成没有基于其预先存在的知识的事实。在这项工作中,Google研究了这种暴露在新知识下对微调后模…

PCB的盘中孔

目录 一、什么时候可以在焊盘上打孔&#xff1f; 二、什么时候可以在焊盘上打孔&#xff1f; 绘制PCB时经常会遇到空间不够无法走线&#xff0c;这时我们会放置过孔使信号线穿过电路板一侧到达另一侧进行走线&#xff0c;这样既方便走线&#xff0c;也能够节省板子空间。有时…

Python悬置动刚度模拟及复数绘制

Python悬置动刚度模拟及复数绘制 1、复数绘制极坐标图2、动刚度的计算公式3、悬置动刚度的影响因素4、 AVL Excite 悬置动刚度的模拟 1、复数绘制极坐标图 # _*_ coding:UTF-8 _*_import matplotlib.pyplot as plt import numpy as np# 定义复数数组 complexNums [1.5 1.2j,…

PyCharm运行程序遇到‘[WinError 1455] 页面文件太小’的问题

最近在云环境的PyCharm运行程序&#xff0c;第一次遇到了WinError 1455的问题&#xff0c;感谢大神们给出的解决方法&#xff0c;特此记录一下。 错误提示是‘页面文件小’导致的问题&#xff0c;那么将页面调大即可。 电脑默认情况下没给D盘分配虚拟内存, 如果Python装在D盘…

遨游 JavaScript 对象星际:探索面向对象编程的深邃世界

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;JavaScript 精粹 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; 文章目录 &#x1f4af;面向对象编程&#x1f517;1 什么是对象&#x1f517;2 什么是…