深入理解模板进阶:掌握C++模板的高级技巧

news2024/9/21 14:49:57

🎉个人名片:

🐼作者简介:一名乐于分享在学习道路上收获的大二在校生
🙈个人主页🎉:GOTXX
🐼个人WeChat:ILXOXVJE
🐼本文由GOTXX原创,首发CSDN🎉🎉🎉
🐵系列专栏:零基础学习C语言----- 数据结构的学习之路----C++的学习之路
🐓每日一句:如果没有特别幸运,那就请特别努力!🎉🎉🎉 ————————————————

🎉文章简介:

🎉本篇文章将 C++模板进阶,全特化,偏特化,非类型模板参数,模板的分离编译 相关知识进行分享!
💕如果您觉得文章不错,期待你的一键三连哦,你的鼓励是我创作动力的源泉,让我们一起加 油,一起奔跑,让我们顶峰相见!!!🎉🎉🎉
——————————————————

一.文章前言

上次将模初阶的学习知识进行了分享(链接: link),今天在这篇文章中你将学习到一些关于C++模板进阶的一些知识,包括模板特化和偏特化:介绍如何通过特化和偏特化来为特定类型提供定制化的模板实现,以及如何处理模板的重载和优先级问题。

二.模板

一. 非类型模板参数

首先,模板参数可以分为类类型形参和非类型形参

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

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

就比如:我们实现一个静态的栈的时候,定义一个静态数组,假设数组大小为N,N被#define为20,那么当我们想要实现一个大小为20的栈和一个大小为1000的栈时,只能被迫去改N的大小,但是如果改为1000的,栈空间为20的那个栈空间就会浪费,这个时候就可以使用非类型模板参数;

例如:

//假设实现一个静态栈
#define N 20
template<class T>
class stack
{
public:
	stack()
	{
		cout << "stack()" << endl;
	}
private:
	T _a[N];
	size_t _size;
};
int main()
{
	stack<int> stack1;   //20
	stack<int> stack2;   //100
	return 0;
}

解决方法:使用非类型模板参数

//使用非类型模板参数,整型常量
template<class T,size_t N>
class stack
{
public:
	stack()
	{
		cout << "stack()" << endl;
	}
private:
	T _a[N];
	size_t _size;
};
int main()
{
	stack<int,20> stack1;   //20
	stack<int,1000> stack2;   //1000

	return 0;
}

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

例如:
在这里插入图片描述

二. 模板的特化

使用模板实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要进行特殊处理的方法;

例子:
//实现了一个日期类

class Date
{
public:
	Date(int year,int month,int day)
		:_year(year)
		,_month(month)
		,_day(day)
	{}
	bool operator>(Date& dd)
	{
		if (_year > dd._year)
			return true;
		else if(_year == dd._year && _month > dd._month)
			return true;
		else if(_year == dd._year && _month == dd._month && _day > dd._day)
			return true;
		else
			return false;
	}
private:
	int _year;
	int _month;
	int _day;
};

我们想要这样做比较时就会出错;

template<class T>
bool great(T x,T y)
{
	return x > y;
}
int main()
{
	Date d1(1010, 5, 3);
	Date d2(1010, 1, 2);
	cout << great(d1, d2)<< endl;

	Date* p1 = &d1;
	Date* p2 = &d2;   //当我们只有日期类对象的指针时,想要比较时
   
	cout << great(p1, p2) << endl;

	return 0;
}

解析:

如图:当我们调用函数1时,参数传的是一个日期类对象,T是一个日期类对象,当在>比较的时候,因为Date是自定义类型,就会去调用他的>运算符重载,实现比较。
但是当在调用第二个函数时,我们想的是对指针指向的对象进行比较,但是函数参数传的是Date的指针,great函数会实例化生成一个Date*的函数,去调用生成的函数,达不到想要的效果;
在这里插入图片描述

一.函数模板的特化

为了解决上面的问题:

1.我们可以使用仿函数类解决(仿函数在上篇分享过,不知道的可以去看看链接: link)

2.函数模板的实例化

template<class T>
bool great(T& x,T& y)
{
	return x > y;
}
//函数特化
template<>
bool great<Date*>(Date*& x, Date*& y)
{
	return *x > *y;
}

3.根据编译器的匹配机制再写一个函数即可这 ;

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

template<class T>
bool great(T& x,T& y)
{
	return x > y;
}
//
bool great(Date* x, Date* y)
{
	return *x > *y;
}

类模板的特化

类模板的特化分为:全特化和偏特化

全特化

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

template<class T1, class T2>
class Data
{
public:
	Data() { }
private:
	T1 _d1;
	T2 _d2;
};

//特化后
template<>
class Data<int, char>
{
public:
	Data() { }
private:
	int _d1;
	char _d2;
};
void test()
{
	Data<int, int> d1;   //调用原模版
	Data<int, char> d2;   //调用特化后的
}
偏特化

偏特化:任何针对模版参数进一步进行条件限制设计的特化版本

偏特化也有两种:
第一种:部分特化
将模板参数列表中部分特化

//原模版
template<class T1, class T2>
class Data
{
public:
	Data() { }
private:
	T1 _d1;
	T2 _d2;
};
//特化后
template<class T1>
class Data<T1,char>
{
public:
	Data(){ }
private:
	T1 _d1;
	char _d2;
};

第二种:参数更进一步的限制
偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本;

例如下面的例子,特化后只能接受指针类型;

//原模版
template<class T1, class T2>
class Data
{
public:
	Data() { }
private:
	T1 _d1;
	T2 _d2;
};
//特化后
template<class T1,class T2>
class Data<T1*,T2*>
{
public:
	Data() { }
private:
	T1 _d1;
	T2 _d2;
};

三. 模板的分离编译

重要的知识开头说:
1.模板不支持分离编译,所以模板函数声明和定义需要放同一文件中;
2.因为模板会被编译两次,所以不支持分离编译;

什么是分离编译?

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

当我们写一个函数模板时,.h放声明,.cpp放定义时,例如:

在这里插入图片描述

最后编译器会报链接错误;

这是因为:
C/C++程序要运行,一般会经历预处理–>编译–>汇编–>链接这四个步骤,

预处理:
主要是头文件的展开,这里的话Test.h,会在main.cpp与Test.cpp里面展开,生成main.i与Test.i,因为头文件展开了,就没有头文件了;
展开了过后Test.i里面既有函数声明也有函数定义;main.i里面有函数声明,没有定义;

编译:
检查语法,实例化模板等操作,如果没有错误后会形成汇编代码;生成main.s与Test.s;实例化模板的时候,这里不知道实例化为什么类型的函数没所以这里并没有实例化函数;

汇编:
生成二进制的机器语言;生成mian.o与Test.o

链接:会将这两个文件合并到一起,到这里会发现函数找不到函数的地址,这两个文件前面3不都是分离的,没有交互,mian.i里面知道将func函数模板中T实例化为int,但是没有函数的定义,只有声明,Test.i可以实例化生成函数,但是不知道将T实例化为什么类型,所以到最后的合并后,没有函数地址,报链接错误;

最好的解决方法,函数模板就不要声明定义分离;

请添加图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于springboot的员工绩效考核管理系统(含源文件)

&#xff08;源码附文章底部&#xff09; 摘 要 科学时代的发展改变了人类的生活&#xff0c;促使网络与计算机技术深入人类的各个角落&#xff0c;得以普及到人类的具体生活中&#xff0c;为人类的时代文明掀开新的篇章。本系统为月度员工绩效考核管理系统&#xff0c;是专为…

JAVA入门第一步2.0

一、JAVA中的关键字 Java中的关键字是Java编程语言中预先定义并保留的单词&#xff0c;它们具有特殊的含义&#xff0c;不能用作变量名、方法名或类名等标识符。以下是我查到的Java中的一些主要关键字&#xff1a; 由于我还在入门&#xff0c;所以所接触的关键字不多&#xf…

从0到1实现RPC | 02 RpcConsumer的远程调用

一、RPC的简化版原理如下图&#xff08;核心是代理机制&#xff09;。 1.本地代理存根: Stub 2.本地序列化反序列化 3.网络通信 4.远程序列化反序列化 5.远程服务存根: Skeleton 6.调用实际业务服务 7.原路返回服务结果 8.返回给本地调用方 二、新建一个模块rpc-demo-c…

如何真正改变自己? 《掌控习惯》

维持改变 1.心态 目标与体系&#xff0c;谁是真正通往成功的钥匙&#xff1f; 2.行动 习惯转变的3个层次 身份 你要成为谁&#xff1f; 你为成为他而幸福吗&#xff1f;过程结果 习惯的基本原理&#xff1a;要重视微小的改变 维持改变成两个方面入手 一、心态&#xff1a;忽略…

【算法刷题】Day33

文章目录 1. 最长湍流子数组题干&#xff1a;算法原理&#xff1a;1. 状态表示&#xff1a;2. 状态转移方程3. 初始化4. 填表顺序5. 返回值 代码&#xff1a; 2. 最长递增子序列题干&#xff1a;算法原理&#xff1a;1. 状态表示&#xff1a;2. 状态转移方程3. 初始化4. 填表顺…

详解rtklib中main函数如何配置文件

目录 Step1&#xff1a;如何给rtklib中的主函数 rnx2rtkp 传参 Step2&#xff1a;给配置选项结构体赋默认值 Step3&#xff1a;继续配置 Step4&#xff1a;寻找 main 函数参数中的 -k Step5&#xff1a;依次遍历参数 Step1&#xff1a;如何给rtklib中的主函数 rnx2rtkp 传参…

SpringBoot接口防止重复提交(AOP+Redis)

方法一&#xff1a; 若依框架的实现 【具体靠请求地址URL参数列表来判断请求是否重复】 SpingBoot接口防止重复提交_springboot接口防抖(防重复提交)的一些实现方案-CSDN博客文章浏览阅读518次。3.根据缓存键获取缓存中对象&#xff0c;如果存在&#xff0c;判断当前请求参…

156.乐理基础-和弦固定标记法(五)挂留(sus)和弦省略音(omit)和弦

如果到这五线谱还没记住还不认识的话去看102.五线谱-高音谱号与103.五线谱-低音谱号这两个里&#xff0c;这里面有五线谱对应的音名&#xff0c;对比着看 如果一章没落下&#xff0c;看到这里&#xff0c;但是看不懂什么意思&#xff0c;那就强行下看&#xff0c;看着看着指不…

【Linux操作系统】命令的运行原理

文章目录 shell命令以及运行原理Linux系列学习目录 shell命令以及运行原理 Linux严格意义上说的是一个操作系统&#xff0c;我们称之为“核心&#xff08;kernel&#xff09;“ &#xff0c;但我们一般用户&#xff0c;不能直接使用kernel。而是通过kernel的“外壳”程序&…

怎么选择陪诊陪护APP开发公司

随着科技的发展&#xff0c;陪诊陪护APP已经成为了人们日常生活中不可或缺的一部分。它为患者提供了便捷的陪诊服务&#xff0c;也为医护人员提供了更多的工作机会。然而&#xff0c;市场上的陪诊陪护APP开发公司众多&#xff0c;如何选择一家专业且有实力的公司成为了消费者的…

DenseNet《Densely Connected Convolutional Networks》

DenseNet学习笔记 摘要引言DenseNetsResNetsDense connectivityResNet 和 DenseNet 的对比DenseBlock 向前传播的过程Composite functionPooling layersGrowth rateBottleneck layersCompression 压缩实现细节 实验训练 代码复现 摘要 最近的研究表明&#xff0c;如果卷积网络…

实现:mysql-5.7.42 到 mysql-8.2.0 的升级(rpm方式)

实现&#xff1a;mysql-5.7.42 到 mysql-8.2.0 的升级&#xff08;rpm方式&#xff09; 1、升级准备1、使用mysql-shell 检查工具检查兼容性 2、操作环境3、备份数据库、my.cnf文件&#xff0c;停止mysql服务&#xff08;重要&#xff09;4、上传、解压安装包5、查看已安装的my…

如何根据业务需求选择合适的电子合同平台?

在数字化转型的浪潮中&#xff0c;电子合同已经成为企业运营中不可或缺的一部分。然而&#xff0c;面对市场上众多的电子合同平台&#xff0c;企业如何根据自身的业务需求做出合适的选择呢&#xff1f;本文将为您一一解答。 在电子合同的签署过程中&#xff0c;数字证书颁发机…

AI论文速读 |(Mamba×时空图预测!) STG-Mamba:通过选择性状态空间模型进行时空图学习

&#xff08;来了来了&#xff0c;虽迟但到&#xff0c;序列建模的新宠儿mamba终于杀入了时空预测&#xff01;&#xff09; 论文标题&#xff1a;STG-Mamba: Spatial-Temporal Graph Learning via Selective State Space Model 作者&#xff1a;Lincan Li, Hanchen Wang&…

WeTrade众汇简单总结保证金和杠杆

通过之前的文章&#xff0c;相信各位投资者都已经明白了保证金和杠杆的含义。今天让WeTrade众汇继续简单总结一下: 财务杠杆是由经纪人提供的无息贷款&#xff0c;允许购买更多的资产或减少保证金&#xff0c;节省经纪人作为抵押品保留的资金。 保证金是交易者由经纪人保留作…

第十节HarmonyOS 常用容器组件3-GridRow

1、描述 栅格容器组件&#xff0c;仅可以和栅格子组件&#xff08;GridCol&#xff09;在栅格布局场景中使用。 2、子组件 可以包含GridCol子组件。 3、接口 GridRow(options:{columns: number | GridRowColumnOption, gutter?: Length | GutterOption, Breakpoints?: B…

企业管理:如何防止私加客户、飞单私单

1、聚合聊天与聚合管理 多个微信号可以在一界面聚合聊天&#xff0c;不用来回切换账号&#xff0c;还可以设置常用的快捷回复提高与客户沟通的效率&#xff0c;右侧可备注客户信息及跟进情况&#xff0c;也可以查看好友朋友圈&#xff0c;素材库可保存图片、视频链接方便随时可…

MATLAB机器学习工具箱——傻瓜式操作

一、使用回归学习期预测北京二手房房价 软件&#xff1a;MATLAB R2023 a 数据&#xff1a; 第一步&#xff1a;导入原始数据和待预测数据 第二步 &#xff1a;打开工具箱中的回归学习器导入学习数据 1.新建会话 2.寻找导入learning data 3.自动锁定前7列为自变量&#xff…

Day17:LeedCode 110.平衡二叉树 257.二叉树的所有路径 404.左叶子之和

110. 平衡二叉树 给定一个二叉树&#xff0c;判断它是否是 平衡二叉树 平衡二叉树:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。 思路: 二叉树节点的深度&#xff1a;指从根节点到该节点的最长简单路径边的条数。二叉树节点的高度&#xff1a;指从该节点到叶…

详解Python的函数嵌套

Python语言允许在定义函数的时候&#xff0c;其函数体内又包含另外一个函数的完整定义&#xff0c;这就是我们通常所说的嵌套定义。 实例1&#xff1a; def OutFun(): #定义函数OutFun()&#xff0c;m3 #定义变量m3;def InFun(): #在OutFun内定义函…