C++:模板(2)

 

目录

非类型模板参数

模板的特化

概念

函数模板特化

类模板特化

全特化

偏特化

模板的分离编译

分离编译的概念

模板的分离编译

​编辑

模板总结


非类型模板参数

模板参数分为类型形参与非类型形参。

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

template<class T>
void swap(T& x, T& y)
{
	T tmp = x;
	x = y;
	y = tmp;
}

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

template<class T, size_t N = 30>
class A
{
private:
	T _array[N];
};

int main()
{
	A<int> a1; // N = 30

	A<int, 10> a2; // N = 10;

	return 0;
}

注意

1.非类型模板参数给的常量的缺省值只能是整型常量不能是浮点数、类对象和字符串等。

2.非类型模板参数必须在编译期就能确认结果。

以下用法是错误的,因为N不确定。

模板的特化

概念

通常情况下,模板可以实现一些与类型无关的代码,但对于一些特殊类型(比如指针)可能会得到一些错误的结果。

就比如说一个小于的比较函数模板。

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

​

对于普通的int、double等类型,这个函数可以得到正确结果。

cout << boolalpha << Less(3, 4) << endl;
cout << boolalpha << Less(1.0, 5.0) << endl;

但如果对于指针类型,那结果就有问题了。

int a = 8;
int b = 4;
cout << boolalpha << Less(&a, &b) << endl;

很明显,这个结果是错的(正确结果应该是false),因为这个Less函数比较的是a、b的地址,没有比较指针指向的内容。

为了解决这种问题,就引入了模板特化了。

模板特化:在原模板的基础上,针对特殊类型进行特殊化的实现方式。

模板特化分为函数模板特化与类模板特化。

函数模板特化

函数模板特化的条件

1.一个基础的函数模板。

2.关键字template后面接空<>。

3.函数名后跟<>,<>里指定特化的类型。

4.函数形参表必须要和模板函数的基础类型参数完全相同,不同的话,编译器会报错。

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

//对Less函数模板进行特化
template<>
bool Less<int*>(int* pa, int* pb)
{
	return *pa < *pb;
}

进行特化后上面的例子就得到了正确答案了。

int a = 8;
int b = 4;
cout << boolalpha << Less(&a, &b) << endl;

但是一般情况下函数模板遇到不能处理或者处理有误的类型时,直接将该类型的函数给出。

bool Less(int* pa, int* pb)
{
	return *pa < *pb;
}

这种实现简单明了,代码可读性高,而对于一些参数类型复杂的函数模板,特化时比较麻烦,因此函数模板不建议特化。

类模板特化

类模板特化也分全特化和偏特化。

全特化

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

template<class T1, class T2>
class Data
{
public:
	Data()
	{
		cout << "Data<T1, T2>" << endl;
	}
private:
	T1 _x;
	T2 _y;
};

template<>
class Data<int, char>
{
public:
	Data()
	{
		cout << "Data<int, char>" << endl;
	}
private:
	int _x;
	char _y;
};

全特化就是将T1和T2确定为int和char。

//匹配原始类模板
Data<int, double> D1;

//匹配特化的类模板
Data<int, char> D2;

偏特化

有两种表现表现方式

部分特化:将模板参数列表中的一部分参数特化

template<class T1, class T2>
class Data
{
public:
	Data()
	{
		cout << "Data<T1, T2>" << endl;
	}
private:
	T1 _x;
	T2 _y;
};

template<class T1>
class Data<T1, int>
{
public:
	Data()
	{
		coutv << "Data<T1, int>" << endl;
	}
private:
	T1 _x;
	int _y;
};

将第二个参数T2特化成int,只要第二个参数类型是int就匹配特化版本。

//匹配原始类模板
Data<int, double> D1;
Data<double,char> D2;
cout << endl;

//匹配部分特化的类模板
Data<int, int> D3;
Data<char*, int> D4;
Data<double, int> D5;

参数限制:针对模板参数更进一步的条件限制设计出来的一个特化版。

template<class T>
class Less
{
public:
	bool operator()(const T& x, const T& y)
	{
		return x < y;
	}
};

//偏特化,对参数类型做出进一步的限制
template<class T>
class Less<T*>
{
public:
	bool operator()(const T* pa, const T* pb)
	{
		return *pa < *pb;
	}
};

这里进行了一个偏特化,将模板参数T限制为T*,这样在传入指针类型时就会匹配偏特化版本,对指针指向的内容进行比较,而不是存储的地址。

运行例子

int a = 3;
int b = 10;

cout << boolalpha << Less<int>()(a, b) << endl;

//匹配偏特化
cout << boolalpha << Less<int*>()(&a, &b) << endl;

模板的分离编译

分离编译的概念

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

模板的分离编译

对于一般的分离编译,我们可以将函数的声明和定义分离,但是对于模板,我们将函数的声明和定义分离会导致链接错误。

原因如下:

func.h 函数声明

func.cpp 函数定义

test.cpp 调用函数

报链接错误的直接原因就是链接时,符号表没有对应函数的地址。

1.代码开始编译的时候,首先就预处理,把头文件展开、宏替换、条件编译、去掉注释,.h和对应对的.cpp文件合在一起生成.i文件;

2.然后就到编译,根据语法树,检查语法,生成对应对的汇编代码,模板这时候问题就出在这,函数的.i文件,有声明有定义,没有具体类型,test.i中有函数的声明,有类型,但是没有定义,所以就不能生成具体的函数符号表也就没有对应的地址,函数.i文件普通函数有声明有定义有类型,可以生成,这时test.i还是转换成汇编 call func(?),等着链接时把地址连接上,也没有报错,由.i文件生成.s文件;

3.编译完就到了汇编,汇编代码转换成二进制机械码,生成.obj文件;

4.链接时把目标文件合并在一起生成可执行程序,并把需要的函数地址等连接上。

解决方法:声明和定义不分离(推荐);模板定义的位置显式实例化。

模板总结

模板的优点

1.代码可以复用,节省资源,提高效率,便于更快迭代开发,C++标准模板(STL)因此而生。

2.代码更灵活

模板的缺点:

1.代码膨胀,编译时间变长。

2.模板出现错误时,信息容易错乱,不利于排查。


拜拜,下期再见😏

摸鱼ing😴✨🎞

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

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

相关文章

STM32L1x 片上温度传感器采用ADC及工厂校准数据提升测量温度精度

背景 由于项目临时需要温度数据&#xff0c;又不想改动硬件了&#xff0c;反正对温度精度要求不算太高&#xff0c;索性就用MCU片上温度传感器的温度&#xff0c;来替代了。这里自己根据网上帖子做了一些测试用例尝试测温&#xff0c;但是&#xff0c;效果都不理想。发现ST官方…

得物App3D博物馆亮相“两博会”,正品保障助力消费体验升级

近日&#xff0c;2024中国体育文化博览会、中国体育旅游博览会&#xff08;以下简称“两博会”&#xff09;在苏州国际展览中心盛大开幕。本次展会汇聚了众多国内外体育文化、体育旅游领域的顶尖企业和品牌&#xff0c;共同展示体育产业的发展成果和最新趋势。在C展馆C21展位&a…

Adams函数构建器(Function Builder)教程来了

学会使用函数构建器是在进行Adams仿真分析的必备技能&#xff0c;通过函数构建器可以查询和使用Adams的各种设计时函数和运行时函数&#xff0c;并能够构建用户自己的函数&#xff0c;大多数情况下的力或者驱动都不是简单的数字&#xff0c;而是需要函数来驱动的&#xff0c;那…

GEE数据集:2001年-2019年全球土地覆被估算(GLanCE)

目录 简介 数据集说明 空间信息 代码1 代码2 代码链接 APP链接 结果 引用 许可 网址推荐 知识星球 机器学习 简介 全球土地覆被估算&#xff08;GLanCE&#xff09; 全球土地覆被估算&#xff08;GLanCE&#xff09;数据集利用 30 米空间分辨率的大地遥感卫星图…

【拯救头痛大作战!有效应对焦虑引发的“脑内风暴”】

在这个快节奏、高压力的时代&#xff0c;焦虑似乎成了许多人难以摆脱的“隐形伴侣”。它不仅悄无声息地侵蚀着我们的心理健康&#xff0c;还可能引发一系列生理反应&#xff0c;其中最常见也最让人苦恼的便是——焦虑导致的头疼。今天&#xff0c;就让我们一起探索如何有效应对…

Linux介绍及常用命令

Linux 系统简介 1969 年&#xff0c;AT&T 公司的⻉尔实验室P MIT 合作开发的 Unix&#xff0c;在于创建⼀个⽤于⼤型、并⾏、多⽤户的操作系统Unix 的推⼴&#xff1a;从学校⾛进企业Unix 的版本要两个&#xff1a; AT&T System V ——就是俗称的 系统 5Berkley Soft…

【数据结构】-数组

数组 特点&#xff1a; 数组的地址连续&#xff0c;可以通过下标获取数据。 1. 数组扩容 步骤&#xff1a; $1. 创建一个比原来数组更长的新数组 $2. 让原来数组当中的数据依次复制到新数组当中 $3. 让arr指向新数组&#xff0c;原数组空间释放 2. 数组插入 2.1 最后位置…

kotlin 入门总结

目录 1、构造函数 2、数据类 data class&#xff0c; 3、object 单例类&#xff0c;相当于java线程安全的懒加载 4、companion object 伴生对象&#xff0c;类似于包装静态值的一个区域块 5、解构 6、空安全 7、条件语句 8、集合 9 属性和支持属性 属性 支持属性 10 …

HarmonyOS Next模拟器异常问题及解决方法

1、问题1&#xff1a;Failed to get the device apiVersion. 解决方法&#xff1a;关闭模拟器清除用户数据重启

【java】数组(超详细总结)

目录 一.一维数组的定义 1.创建数组 2.初始化数组 二.数组的使用 1.访问数组 2.遍历数组 3.修改数据内容 三.有关数组方法的使用 1.toString 2. copyOf 四.查找数组中的元素 1.顺序查找 2.二分查找binarySearch 五.数组排序 1.冒泡排序 2.排序方法sort 六.数组逆置…

redis的配置文件解析

我的后端学习大纲 我的Redis学习大纲 1.1.Redis的配置文件&#xff1a; 1.Redis的配置文件名称是&#xff1a;redis.conf 2.在vim这个配置文件的时候&#xff0c;默认是不显示行号的&#xff0c;可以编辑下面这个文件&#xff0c;末尾加上set nu&#xff0c;就会显示行号: 1.…

React源码03 - React 中的更新

03 - React 中的更新 React 中创建更新的方式&#xff1a; 初次渲染&#xff1a;ReactDOM.render、ReactDOM.hydrate 后续更新&#xff1a;setState、forceUpdate 1. ReactDOM.render() 先创建 ReactRoot 顶点对象然后创建 FiberRoot 和 RootFiber创建更新&#xff0c;使应用进…

Qt | http获取网页文件(小项目)

点击上方"蓝字"关注我们 ctrl+r 运行 URL可以自己替换一个试一试 【源码获取】 链接:https://pan.baidu.com/s/1QzHKZPXjkpx2p5TWUS_acA?pwd=5xsd 提取码:5xsd 01、QProgressDialog >>> QProgressDialog 是 Qt 框架中的一个类,主要用于显示一个进…

Java使用dom4j生成kml(xml)文件遇到No such namespace prefix: xxx is in scope on:问题解决

介绍addAttribute和addNamepsace: addAttribute 方法 addAttribute 方法用于给XML元素添加属性。属性&#xff08;Attributes&#xff09;是元素的修饰符&#xff0c;提供了关于元素的额外信息&#xff0c;并且位于元素的开始标签中。属性通常用于指定元素的行为或样式&#…

Golang | Leetcode Golang题解之第497题非重叠矩形中的随机点

题目&#xff1a; 题解&#xff1a; type Solution struct {rects [][]intsum []int }func Constructor(rects [][]int) Solution {sum : make([]int, len(rects)1)for i, r : range rects {a, b, x, y : r[0], r[1], r[2], r[3]sum[i1] sum[i] (x-a1)*(y-b1)}return Sol…

ReactOS系统中搜索给定长度的空间地址区间中的二叉树

搜索给定长度的空间地址区间 //搜索给定长度的空间地址区间 MmFindGap MmFindGapTopDown PVOID NTAPI MmFindGap(PMADDRESS_SPACE AddressSpace,ULONG_PTR Length,ULONG_PTR Granularity,BOOLEAN TopDown );PMADDRESS_SPACE AddressSpace,//该进程用户空间 ULONG_PTR Length,…

JavaScript入门中-流程控制语句

本文转载自&#xff1a;https://fangcaicoding.cn/article/52 大家好&#xff01;我是方才&#xff0c;目前是8人后端研发团队的负责人&#xff0c;拥有6年后端经验&3年团队管理经验&#xff0c;截止目前面试过近200位候选人&#xff0c;主导过单表上10亿、累计上100亿数据…

echart改变最后一个节点的图标

需求 在折线图的最后一个节点增加一个gif动图表示增长 一、静态图的使用 采用symbol属性进行设置&#xff0c;结果就是只能展示静态图 无法插入gif series: [{data: [150, 230, 224, 218, 135, 147, {value:200,symbol:image://https://ylxstatic.storage.ylingxin.com/va…

PostgreSQL数据库查看shared buffer配置

哈喽&#xff0c;大家好&#xff0c;我是木头左&#xff01; PostgreSQL是一个功能强大的开源关系型数据库管理系统&#xff0c;广泛应用于各种规模的应用程序。在PostgreSQL中&#xff0c;shared buffer是一个重要的性能调优参数&#xff0c;它直接影响到数据库查询的性能。本…

【信息论基础第六讲】离散无记忆信源等长编码包括典型序列和等长信源编码定理

一、信源编码的数学模型 我们知道信源的输出是消息序列&#xff0c;对于信源进行编码就是用码字集来表示消息集&#xff0c;也就是要进行从消息集到码字集的映射。 根据码字的特征我们又将其分为D元码&#xff0c;等长码&#xff0c;不等长码&#xff0c;唯一可译码。 我们通过…