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

🎉个人名片:

🐼作者简介:一名乐于分享在学习道路上收获的大二在校生
🙈个人主页🎉: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;
};

三. 模板的分离编译

什么是分离编译?

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

当我们写一个函数模板时,.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实例化为什么类型,所以到最后的合并后,没有函数地址,报链接错误;

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

请添加图片描述

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

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

相关文章

禅道二次开发——创建需求

获取Token 官网参考 https://www.zentao.net/book/api/setting-369.html post http://xxx:8442/zentaopms/www/api.php/v1/tokensbody {"account": "xxx", "password": "xxx"}结果如下图 创建需求 官网参考 https://www.zentao.…

DBA面试题:MySQL缓存池LRU算法做了哪些改进?

下图是MySQL&#xff08;MySQL5.7版本&#xff09;体系架构图 MySQL的InnoDb Buffer Pool 缓冲池是主内存中的一个区域&#xff0c;用来缓存InnoDB在访问表和索引时的数据。对于频繁使用的数据可以直接从内存中访问&#xff0c;从而加快处理速度。如果一台服务器专用作MySQL数据…

Footprint Analytics 强势入局 AI 推出全新投研工具

作者&#xff1a;lesleyfootprint.network 3 月 15 日&#xff0c;链上数据平台 Footprint Analytics 宣布入局 AI&#xff0c;推出了旗下首款 AI 投研工具—— Pea.AI。 作为专门服务于区块链行业的 AI 平台&#xff0c;Pea.AI 旨在提升加密资产投资领域的数据流动性和知识…

c语言实现https客户端 源码+详细注释(OpenSSL下载,visual studio编译器环境配置)

OpenSSL的下载和环境配置 请参考&#xff1a;openssl下载安装教程 步骤&#xff1a;官网下载->安装到选定目录->配置环境变量->打开命令窗口检查是否安装成功 注意&#xff1a; 打开命令窗口&#xff08;快捷键winr,在弹出窗口内输入cmd按回车&#xff09;&#xff0…

leetcode代码记录(长度最小的子数组

目录 1. 题目&#xff1a;2. 我的代码&#xff1a;小结&#xff1a; 1. 题目&#xff1a; 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 target 的长度最小的 连续 子数组 [numsl, numsl1, …, numsr-1, numsr] &#xff0c;并返回其…

外贸业务员如何说服老板拿到更低价

小伙伴问我说如何说服老板给到更好的价格&#xff0c;这个问题呢我在这里说一下我的观点 第一你需要去分析这个客户到底值不值得我们去给他花更多的一些心思&#xff0c;因为客户想要的这个价格既然已经突破了公司的价格标准了&#xff0c;说明他的价格要的非常的低&#xff0…

每日一题——LeetCode1704.判断字符串的两半是否相等

方法一 计数 从头到尾简历字符串&#xff0c;判断字符是否是元音字符&#xff0c;如果是再判断i的值是字符串前一半还是后一半&#xff0c;前一半sum1&#xff0c;后一半sum2 var halvesAreAlike function(s) {const h "aeiouAEIOU";let sum1 0, sum2 0;for (l…

TypeScript:typescript的安装与运行

TypeScript&#xff1a;typescript的安装与运行 1 安装方式 -g全局安装TypeScript&#xff1a; npm install -g typescript2 运行方式 &#xff08;1&#xff09;ts编译成js&#xff0c;使用node命令运行js文件 打开vscode&#xff0c;进入ts文件所在目录下并打开终端term…

微信小程序 canvas层级过高覆盖原生组件

一、背景 微信小程序中使用signature第三方插件完成签名效果&#xff0c;但真机调试时发现canvas层级过高遮挡了按钮 二、具体问题 问题原因&#xff1a;签名后点击按钮无法生效 问题代码&#xff1a; <template><view class"sign_page" v-cloak>&l…

MATLAB编译器配置:MinGW

使用 MATLAB 2022b版本&#xff0c;查询编译器时如上&#xff0c;想安装个MinGW编译器&#xff0c;自带的附加资源管理不好使&#xff0c;只能换个别的法子&#xff0c;经过一些参考&#xff0c;总结如下。 步骤1.在这里下载一个MinGW.最新版本是10.3.0.然后默认安装&#xff…

【好用】Star超36.8k,一个的免费通用数据库管理工具

关于数据库管理工具&#xff0c;大家可能都在用SQLyog、Navicat、MySQL-Front、SQL Studio、MySQL Workbench等等&#xff0c;这些管理工具不是不好用&#xff0c;就是要变魔术才可以用&#xff0c;今天 V 哥给大家推荐一个即好用&#xff0c;又免费的可视化通用数据库管理工具…

手机备忘录怎么导出到电脑,如何将手机备忘录导出到电脑

备忘录是我们日常生活和工作中常用的工具之一&#xff0c;我们可以在手机上轻松地记录重要的事务、想法和灵感。然而&#xff0c;在某些情况下&#xff0c;我们可能需要将手机备忘录导出到电脑进行更详细的整理和管理。那么&#xff0c;手机备忘录怎么导出到电脑&#xff0c;如…

如何设计一个kafka(理解)

因业务需要而对消息中间件的频繁使用后&#xff0c;每次总会问自己一个问题&#xff1a;kafka为什么快&#xff1f;然后再去背一背八卦找找答案。直到近日终于能站在一个新奇的角度理解kafka&#xff0c;且积累的各种细节串通了起来&#xff0c;实属惊喜。 回到最开始的问题&am…

VS Code安装

VS Code 安装 一、下载 进入VS Code官网&#xff1a;https://code.visualstudio.com&#xff0c;点击 DownLoad for Windows下载windows版本 Stable&#xff1a;稳定版Insiders&#xff1a;内测版 二、安装 双击安装包&#xff0c;选择我同意此协议&#xff0c;再点击下一步 …

【办公类-22-15】周计划系列(5-6)“周计划-06 周计划打印pdf(docx删除内容转PDF)“ (2024年调整版本)

作品展示 背景需求&#xff1a; 前期用docx&#xff08;删除第一页反思部分内容&#xff09;转PDF转png&#xff08;第一页&#xff09;的方式获得上传网页用的图片。 【办公类-22-14】周计划系列&#xff08;5-5&#xff09;“周计划-05 上传周计划png&#xff08;docx转PDF…

2. IS-IS 基础实验

2.1 IS-IS 配置实验 2.1.1 实验介绍 2.1.1.1 学习目标 1. 实现 IS-IS 协议基本配置 2. 实现 IS-IS 协议 DIS 优先级修改 3. 实现 IS-IS 协议网络类型修改 4. 实现 IS-IS 协议外部路由引入 5. 实现 IS-IS 接口 cost 修改 6. 实现 IS-IS 路由渗透配置 2.1.1.2 实验组网介…

【每日一题】1793. 好子数组的最大分数-2024.3.19

题目&#xff1a; 1793. 好子数组的最大分数 给你一个整数数组 nums &#xff08;下标从 0 开始&#xff09;和一个整数 k 。 一个子数组 (i, j) 的 分数 定义为 min(nums[i], nums[i1], ..., nums[j]) * (j - i 1) 。一个 好 子数组的两个端点下标需要满足 i < k < …

【web前端】CSS语法

CSS语法 1. CSS语法格式 通常情况下语法格式如下: 选择器{属性名:属性值;属性名:属性值;属性名:属性值;... }2. CSS添加方式 2.1 行内样式 直接将样式写在本行的标签内。 <h1><p style"font-size: 48px; color:red;";>行内样式测试</p></…

抖音视频批量提取软件|无水印视频下载

抖音视频批量提取软件&#xff0c;让您高效下载精彩内容&#xff01; 您是否经常需要下载抖音视频&#xff0c;但传统的下载方式繁琐且低效&#xff1f;别担心&#xff0c;我们为您提供了一款强大而智能的抖音视频批量提取软件&#xff0c;让您轻松实现下载无水印的精彩内容&am…

瑞云渲染邀请码怎么用?

瑞云渲染邀请码是用来提供注册用户的特殊权限或优惠的代码。最近&#xff0c;瑞云渲染在注册页面开放了填写邀请码的功能。对于新用户而言&#xff0c;在注册账号时可能不太了解邀请码的具体作用以及如何获取邀请码。下面我们一起来了解一下吧&#xff01; 瑞云渲染是什么&…