C++基础知识9 模版进阶

news2024/9/29 17:28:24

模版进阶

  • 1. 非类型模板参数
  • 2. 模板的特化
    • 2.1 概念
    • 2.2 函数模板特化
    • 2.3 类模板特化
      • 2.3.1 全特化
      • 2.3.2 偏特化
      • 2.3.3 类模板特化应用示例
  • 3 模板分离编译
    • 3.1 什么是分离编译
    • 3.2 模板的分离编译
    • 3.3 解决方法
  • 4. 模板总结

1. 非类型模板参数

模板参数分类类型形参与非类型形参。
类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。
非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常
量来使用。

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

注意:

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

2. 模板的特化

2.1 概念

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些
错误的结果,需要特殊处理,比如:实现了一个专门用来进行小于比较的函数模板

namespace bite
{
 // 定义一个模板类型的静态数组
 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;
 }}
// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{
 return left < right;
}
int main()
{
 cout << Less(1, 2) << endl;  
 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;
}

我们在类与对象实现了日期类
可以看到,Less绝对多数情况下都可以正常比较,但是在特殊场景下就得到错误的结果。上述示
例中,p1指向的d1显然小于p2指向的d2对象,但是Less内部并没有比较p1和p2指向的对象内
容,而比较的是p1和p2指针的地址,这就无法达到预期而错误。

此时,就需要对模板进行特化

即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化与类模板特化。

2.2 函数模板特化

函数模板的特化步骤:

  1. 必须要先有一个基础的函数模板
  2. 关键字template后面接一对空的尖括号<>
  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
  4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇 怪的错误。
// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{
	return left < right;
}
// 对Less函数模板进行特化
template<>
bool Less<Date*>(Date* left, Date* right)
{
	return *left < *right;
}
int main()
{
	cout << Less(1, 2) << endl;
	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;
}

注意:一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该
函数直接给出

bool Less(Date* left, Date* right)
{
	return *left < *right;
}

该种实现简单明了,代码的可读性高,容易书写。对于一些参数类型复杂的函数模板,相较于特化
不如直接实现函数

2.3 类模板特化

2.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; }
private:
	int _d1;
	char _d2;
};

2.3.2 偏特化

偏特化:任何针对模版参数进一步进行条件限制设计的特化版本。比如对于以下模板类:
偏特化有以下两种表现方式:

  • 部分特化
    将模板参数类表中的一部分参数特化。
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;
}; 
  • 参数更进一步的限制
    偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一
    个特化版本。
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;    
 };

注意先有模版后有特化。

2.3.3 类模板特化应用示例

有如下专门用来按照小于比较的类模板Less:

#include<vector>
#include<algorithm>
template<class T>
struct Less
{
	bool operator()(const T& x, const T& y) const
	{
		return x < y;
	}
};
int main()
{
	Date d1(2022, 7, 7);
	Date d2(2022, 7, 6);
	Date d3(2022, 7, 8);
	vector<Date> v1;
	v1.push_back(d1);
	v1.push_back(d2);
	v1.push_back(d3);
	// 可以直接排序,结果是日期升序
	sort(v1.begin(), v1.end(), Less<Date>());
	vector<Date*> v2;
	v2.push_back(&d1);
	v2.push_back(&d2);
	v2.push_back(&d3);

	// 可以直接排序,结果错误日期还不是升序,而v2中放的地址是升序
	// 此处需要在排序过程中,让sort比较v2中存放地址指向的日期对象
	// 但是走Less模板,sort在排序时实际比较的是v2中指针的地址,因此无法达到预期
	sort(v2.begin(), v2.end(), Less<Date*>());
	return 0;
}

通过观察上述程序的结果发现,对于日期对象可以直接排序,并且结果是正确的。但是如果待排
序元素是指针,结果就不一定正确。因为:sort最终按照Less模板中方式比较,所以只会比较指
针,而不是比较指针指向空间中内容,此时可以使用类版本特化来处理上述问题:

// 对Less类模板按照指针方式特化
template<>
struct Less<Date*>
{
 bool operator()(Date* x, Date* y) const
 {
 return *x < *y;
 }
};

特化之后,在运行上述代码,就可以得到正确的结果

使用了虚函数,虚函数其实是一个重载了()的类,在algorithm头文件中包含这两个类,但可能我们所期望的因此此时应该特化

3 模板分离编译

3.1 什么是分离编译

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

3.2 模板的分离编译

假如有以下场景,模板的声明与定义分离开,在头文件中进行声明,源文件中完成定义:

// a.h
template<class T>
T Add(const T& left, const T& right);
// a.cpp
template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}
// main.cpp
#include"a.h"
int main()
{
	Add(1, 2);
	Add(1.0, 2.0);

	return 0;
}

分析:
在这里插入图片描述

3.3 解决方法

  1. 将声明和定义放到一个文件 “xxx.hpp” 里面或者xxx.h其实也是可以的。推荐使用这种。
  2. 模板定义的位置显式实例化。这种方法不实用,不推荐使用。

4. 模板总结

【优点】

  1. 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
  2. 增强了代码的灵活性

【缺陷】

  1. 模板会导致代码膨胀问题,也会导致编译时间变长
  2. 出现模板编译错误时,错误信息非常凌乱,不易定位错误

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

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

相关文章

有些硬盘录像机接入视频汇聚平台EasyCVR后通道不显示/显示不全,该如何处理?

EasyCVR视频监控汇聚管理平台是一款针对大中型项目设计的跨区域网络化视频监控集中管理平台。该平台不仅具备视频资源管理、设备管理、用户管理、运维管理和安全管理等功能&#xff0c;还支持多种主流标准协议&#xff0c;如GB28181、RTSP/Onvif、RTMP、部标JT808、GA/T 1400协…

C#获取变量的数据类型_C#获取对象的数据类型

C#中的数据类型用 Type 类描述&#xff0c;具体的类型是 Type的对象 一、object.GetType() 对象.GetType()-----对象不能为null,否则运行报错&#xff1b; 通用的获取类型方式 底层对象获取类型,所有的对象&#xff0c;变量获取类型&#xff0c;GetType()&#xff1b; 返回的…

JAVA打造全球商品集散地国际版多商户商城系统小程序源码

打造全球商品集散地 —— 国际版多商户商城系统 &#x1f30d;【开篇&#xff1a;连接世界&#xff0c;触手可及】&#x1f30d; 在这个全球化的时代&#xff0c;购物不再受地域限制&#xff0c;每一个消费者都渴望能轻松买到来自世界各地的优质商品。而“打造全球商品集散地”…

快速实现AI搜索!Fivetran 支持 Milvus 作为数据迁移目标

Fivetran 现已支持 Milvus 向量数据库作为数据迁移的目标&#xff0c;能够有效简化 RAG 应用和 AI 搜索中数据源接入的流程。 数据是 AI 应用的支柱&#xff0c;无缝连接数据是充分释放数据潜力的关键。非结构化数据对于企业搜索和检索增强生成&#xff08;RAG&#xff09;聊天…

深度学习500问——Chapter17:模型压缩及移动端部署(1)

文章目录 17.1 模型压缩理解 17.2 为什么需要模型压缩和加速 17.3 模型压缩的必要性及可行性 17.4 目前有哪些深度学习模型压缩方法 17.4.1 前段压缩和后端压缩对比 17.4.2 网络剪枝 17.4.3 典型剪枝方法对比 17.4.4. 网络蒸馏 17.4.5 前端压缩 17.4.6 后端压缩 深度神经网络在…

刚毕业就做项目经理,现在感觉越做越没动力,还有必要坚持下去吗?

那种一毕业就做项目经理的&#xff0c;以为是少走10年弯路&#xff0c;干了一年后&#xff0c;才发现这简直就是在坑自己。没点真材实料&#xff0c;经理也变成了“空中架子”。 因此&#xff0c;很多一毕业就当上项目经理的人&#xff0c;要么是干着干着就转回到技术岗位&…

AI数字人:终于知道视频号咋爆的了

点击下方&#x1f447;“拂晓AI数字人”关注公众号 一起学习AI 数字人&#xff0c;让服务更高效&#xff01; 做视频号是不是有这种感觉&#xff0c;辛辛苦苦剪了几个小时的视频&#xff0c;上去就是几百播放量&#xff0c;就没有流量了&#xff0c;很抓狂。 找别人的爆的视频&…

2.点位管理——帝可得后台管理系统

目录 前言点位管理菜单模块1.需求说明2.库表设计3.生成基础代码0 .使用若依代码生成器最终目标1.创建点位管理2.添加数据字典3.配置代码生成信息4.下载代码并导入项目 4.优化菜单——点位管理1.优化区域管理2.增加点位数 前言 提示&#xff1a;本篇介绍点位管理模块&#xff0…

知识库管理系统盘点:2024年必备10款

在当今信息爆炸的时代&#xff0c;高效地组织、存储、管理和共享知识与信息&#xff0c;已成为企业提升竞争力和实现数字化转型的关键。知识库管理系统&#xff08;Knowledge Base Management System, KBMS&#xff09;作为重要工具&#xff0c;正受到越来越多企业的青睐。本文…

使用bat命令在没有java的环境下启动jar包

使用bat命令在没有java的环境下启动jar包 先看一下目录下面的文件 里面有三个比较重要的文件 clean.bat&#xff1a;用于清除占用程序的端口 一键启动_x64.bat&#xff1a;用于启动全部的项目 jre8_win64&#xff1a;用于jar所需要的java环境 注意事项&#xff1a; 关于jar…

【RabbitMq源码阅读】分析RabbitMq发送消息源码

一&#xff1a;基本介绍 本文通过demo构建测试代码&#xff0c;debug分析的方法查看RabbitMq源码。 rabbit的中文文档&#xff1a; 官方中文文档 二&#xff1a;测试Demo 2.1 引入Springboot整合的RabbitMq依赖 <dependency><groupId>org.springframework.boot…

使用npm link 把一个本地项目变成依赖,引入到另一个项目中

突然有天,发现线上的项目有块功能缺失,我以为是我优化的时候不小心改坏了什么代码,导致的,先上图 第一反应,就以为天塌了,完全无从入手,然后我就找了之前的离职的同事,他又给我两个包,让我打成依赖扔进去,这两个包分别是scratch-blocks,scratch-vm, 然后我就使用了npm link np…

淘宝api上货软件)一刻工具箱,一天上几万不出现爬虫违规,更新开放类目错放功能,淘宝电商必备软件!

天猫淘宝抖音上货神器&#xff0c;助力电商快速铺货 在当今这个信息爆炸、电商飞速发展的时代&#xff0c;如何快速有效地将产品铺货到各大电商平台&#xff0c;成为每一位电商从业者都需要面对的问题。 通过电商API接口能为电商从业者打造的综合辅助工具&#xff0c;支持天猫、…

uniapp学习(002 常用的内置组件)

零基础入门uniapp Vue3组合式API版本到咸虾米壁纸项目实战&#xff0c;开发打包微信小程序、抖音小程序、H5、安卓APP客户端等 总时长 23:40:00 共116P 此文章包含第5p-第p10的内容 文章目录 view组件相当于div标签按下松开例子冒泡例子 text组件 相当于span标签scroll-view纵…

第二百五十八节 JPA教程 - JPA查询选择两个实体示例

JPA教程 - JPA查询选择两个实体示例 以下JPQL从两个实体中选择。 List l em.createQuery("SELECT d, m FROM Department d, Professor m WHERE d m.department").getResultList();例子 以下代码来自Professor.java。 package cn.w3cschool.common;import java.…

想跳槽,我懂你!

在职场的长河中&#xff0c;每个人都是自己航行船的舵手&#xff0c;时而顺流而下享受平静&#xff0c;时而逆流而上追求挑战。跳槽&#xff0c;作为职业生涯中常见且重要的决策之一&#xff0c;往往承载着对现状的不满、对未来的憧憬以及对自我价值的重新定位。本文将从跳槽的…

紫光 FPGA固化RAM位置的操作流程

1. 前提条件&#xff1a;需要已经编译出一个功能完整的没有时序违例的版本出来&#xff1b; 2. 将RAM导出至txt文件&#xff1a; 这个过程需要几分钟&#xff0c;耐心等待一下。 等待提示成功就可以进行下一步操作了。 3. 将【2】中的txt文件中的内容全选复制粘贴到pcf文件的…

离职赔偿一览表-这年头每人都应该备一份

离职赔偿一览表 离职时一定要知道N、N1&#xff0c;2N的计算方法 N&#xff08;经济补偿金&#xff09;、N1&#xff08;经济补偿金代通知金&#xff09;&#xff0c;2N&#xff08;赔偿金&#xff09;其实都是简称。 01 经济补偿金&#xff08;N&#xff09; 经济补偿金工…

led灯什么牌子的质量好?五款市面上非常适合孩子使用的护眼台灯

在当今这个数字化时代&#xff0c;孩子们从小就开始频繁接触各种数码设备&#xff0c;每日长时间面对着电子屏幕。由于疫情的影响&#xff0c;居家上网课更是让孩子们不得不持续面对电子屏幕。而儿童和青少年时期正是眼睛发育的关键阶段&#xff0c;许多孩子因为在这个时期过度…

Python从入门到精通-基础篇

1.Python的起源 1989年&#xff0c;为了打发圣诞节假期&#xff0c;Gudio van Rossum&#xff08;吉多范罗苏姆&#xff08;龟叔&#xff09;&#xff09;决心开发一个新的解释程序&#xff08;Python雏形&#xff09; 1991年&#xff0c;第一个Python解释器诞生 Python这个…