【C++】非类型模板参数、模板特化、模板的分离编译、模板总结

news2024/11/28 16:05:20

文章目录

    • 一、非类型模板参数
    • 二、模板特化
      • 1.函数模板特化
      • 2.类模板特化
    • 三、模板的分离编译
    • 四、模板总结

一、非类型模板参数

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

#define N 10;
//静态数组
template <class T>
class Array
{
private:
    T _a[N];
}

如果是这样的话,我们无法去灵活控制大小

int main()
{
    Array<int> a1;
    Array<int> a2;
    return 0;
}

这都是固定的了,写死的了,所以这时候我们可以使用非类型模板参数

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

//非类型模板参数——常量
template<class T,size_t N>
class Array
{
private:
    T _a[N];
}
int main()
{
    Array<int,10> a1;
    Array<double,100>a2;
    return 0;
}

注意:

  • 非类型模板参数只支持整型(浮点数、类对象以及字符串是不允许作为非类型模板参数的)

  • 非类型的模板参数必须在编译期就能确认结果

实际上库里面的array也是非类型模板:

image-20230110115311577

库里面的array与C语言的数组相比:

int main()
{
    int a1[10];
    array<int,10> a2;
    array<double,10> a3;
    return 0;
}

区别在于:array可以对越界进行检查,C语言数组对于越界读是不检查的,而对于越界写是抽查的(不同平台不一样)。而array可以assert检查是否越界。


二、模板特化

1.函数模板特化

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果

我们来以日期类为例子:

class Date
{
public:
	Date(int year = 1900,int month = 1,int day = 1)
		:_year(year)
		,_month(month)
		,_day(day)
	{}

	bool operator<(const Date& d) const
	{
		return (_year < d._year) ||
			(_year == d._year && _month < d._month) ||
			(_year == d._year && _month == d._month && _day < d._day);
	}

	bool operator>(const Date& d) const
	{
		return (_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day);
	}

	friend ostream& operator<<(ostream& _cout, const Date& d)
	{
		_cout << d._year << "-" << d._month << "-" << d._day;
		return _cout;
	}

private:
	int _year;
	int _month;
	int _day;
};
template<class T>
bool Less(T left, T right)
{
	return left < right;
}

int main()
{
	cout << Less(1, 2) << endl;
	Date d1(2023, 1, 6);
	Date d2(2023, 1, 9);
	cout << Less(d1, d2) << endl;

	Date* p1 = &d1;
	Date* p2 = &d2;
	cout << Less(p1, p2) << endl;
	return 0;
}

image-20230110135553319

所以我们要去对Date*进行特殊化处理——Date*

函数模板的特化步骤:

  1. 必须要先有一个基础的函数模板
  2. 关键字template后面接一对空的尖括号<>
  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
  4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误
//针对某些类型进行特殊处理——Date*
template<>
bool Less<Date*>(Date* left, Date* right)
{
	return *left < *right;
}

函数模板也可以不写成模板,直接写成函数也是可以的,因为函数模板支持重载

2.类模板特化

1.全特化

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

类模板的全特化将模板参数列表中的所有参数我们都将其写出来:

image-20230110142030654

如果此时的数据类型是我们自己定义的,比如我们之前所说的Date*之时,比较的是地址,所以我们之前是通过自己写一个仿函数来实现比较大小的,代码如下:

struct PDateLess
{
	bool operator()(const Date* d1, const Date* d2)
	{
		return *d1 < *d2;
	}
};

struct PDateGreater
{
	bool operator()(const Date* d1, const Date* d2)
	{
		return *d1>*d2;
	}
};
void TestPriorityQueue()
{
    //大堆
	priority_queue <Date*, vector<Date*>, PDateLess> q3;
	q3.push(new Date(2018, 10, 29));
	q3.push(new Date(2018, 10, 28));
	q3.push(new Date(2018, 10, 30));
	cout << *q3.top() << endl;
	//小堆
	priority_queue<Date*, vector<Date*>, PDateGreater> q4;
	q4.push(new Date(2018, 10, 29));
	q4.push(new Date(2018, 10, 28));
	q4.push(new Date(2018, 10, 30));
	cout << *q4.top() << endl;
}

现在,我们如果不写仿函数,这时候就可以通过针对Date*实现特化了:

template <class T>
class Greater
{
public:
    bool operator()(const T& x, const T& y) const
    {
        return x > y;
    }
};
//特化
template<>
class Greater<Date*>
{
    public:
    bool operator()(Date* const& d1, Date* const& d2) const
    {
        return *d1 > *d2;
    }
};
int  main()
{
    hwc::priority_queue<Date*, vector<Date*>, Greater<Date*>> q4;
	q4.push(new Date(2018, 10, 29));
	q4.push(new Date(2018, 10, 28));
	q4.push(new Date(2018, 10, 30));
	cout << *q4.top() << endl;
    return 0;
}

2.偏特化

偏特化:任何针对模版参数进一步进行条件限制设计的特化版本。部分特化将模板参数类表中的一部分参数特化 :

template<class T1,class T2>
class Data
{
public:
	Data()
	{
		cout << "Data<T1,T2>" << endl;
	}
private:
	T1 _d1;
	T2 _d2;
};
//半特化、偏特化
template<class T1>
class Data<T1, char>
{
public:
	Data()
	{
		cout << "Data<T1,char>" << endl;
	}
};
int main()
{
    //模板
    Data<int,int>d;
    //偏特化
    Data<double,char> d1;
    Data<char,char> d2;
    return 0;
}

偏特化可以对参数进一步的限制:只要是指针,不管是什么类型的指针,针对指针,也可以针对引用:

//参数类型进一步限制
template<class T1,class T2>
class Data<T1*, T2*>
{
public:
	Data()
	{
		cout << "Data<T1*,T2*>" << endl;
	}
};

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

int main()
{
    
    return 0;
}
int main()
{
    //指针
	Data<char*, char*> d5;
	Data<double*, int*> d6;
    
    //引用
    Data<double&,int&> d7;
	return 0;
}

特化的本质体现的是编译器的参数匹配原则


三、模板的分离编译

模板的分离编译我们之前就有说过,这里重新说一遍:

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

而对于模板,链接之前并不会交互,分离编译就会导致用的地方.cpp没有实例化,没有实例化就会导致链接不上。

比如:a.h,a.cpp,test.cpp这三个文件,

编译链接过程:预处理——>编译——>汇编——>链接

预处理:去注释,宏替换,头文件展开,条件编译(a.i,test.i)

编译:生成汇编代码(a.s,test.s)、符号汇总

汇编:把汇编变成二进制目标文件(a.o,test.o),形成符号表

链接:符号表的合并与重定位,将多个obj文件合并成一个,形成可执行程序

image-20230110191314742

解决方案:

  1. 将声明和定义放到一个文件 “xxx.hpp” 里面或者xxx.h其实也是可以的。推荐使用这种。此时在编译阶段中,就有了模板的实例化。
  2. 模板定义的位置显式实例化。这种方法不实用,不推荐使用 。如果实例化的类型少那还是可行的,如果要针对的类型很多,那就太麻烦了
  3. image-20230110193321200

四、模板总结

优点: 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生。增强了代码的灵活性。
缺点:模板会导致代码膨胀问题,也会导致编译时间变长。出现模板编译错误时,错误信息非常凌乱,不易定位错误 。

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

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

相关文章

Spring_FrameWork_05(AOP)

Spring整合Junit RunWith(SpringJUnit4ClassRunner.class) ContextConfiguration(classes SpringConfig.class)加载test运行类和spring配置文件 使用Junit提供的Runwith注解&#xff0c;将Junit原有的运行器替换成spring提供的SpringJUnit4ClassRunner。 这个注解的值就是运…

【计算机视觉】Softmax代码实现、过拟合和欠拟合的表现与解决方法

Softmax原理 Softmax函数用于将分类结果归一化&#xff0c;形成一个概率分布。作用类似于二分类中的Sigmoid函数。 对于一个k维向量z&#xff0c;我们想把这个结果转换为一个k个类别的概率分布p(z)。softmax可以用于实现上述结果&#xff0c;具体计算公式为&#xff1a; 对于k…

程序员不了解这些投简历的巨坑,面试注定一开始就失败!

目录 前言第一阶段&#xff1a;练手第二阶段&#xff1a;冲刺第三阶段&#xff1a;收尾 前言 之前写了两篇文章&#xff0c;给大家介绍了一下如何利用短期的时间&#xff0c;尽可能充分的为面试做准备&#xff1a; 1.《我只是把握好了这3点&#xff0c;1个月后成功拿下大厂…

2023春节祝福系列第一弹(下)(放飞祈福孔明灯,祝福大家身体健康)(附完整源代码及资源免费下载)

2023春节祝福系列第一弹&#xff08;下&#xff09; &#xff08;放飞祈福孔明灯&#xff0c;祝福大家身体健康&#xff09; &#xff08;附完整源代码及资源免费下载&#xff09; 目录 四、画一朵真实的祥云 &#xff08;1&#xff09;、画一个渐变的白色径向渐变背景 &a…

外业调查工具助手,照片采集、精准定位、导航、地图查看

你是不是在外业调查时要背着一堆图纸 是不是一不小心图纸污损或丢失&#xff0c;工作又得重做 是不是经常会出现图纸标注的空间不足 是不是外业采集中要携带一大堆繁琐的仪器 是不是每次收集的数据、照片等在整理的过程中发现工作量巨大 是不是经常会出现采集回来的内容跟…

《MySQL 入门教程》第 36 篇 Python 访问 MySQL

本篇我们介绍如何利用 Python DB API 连接和操作 MySQL 数据库&#xff0c;包括数据的增删改查操作、存储过程调用以及事务处理等。 Python 是一种高级、通用的解释型编程语言&#xff0c;以其优雅、准确、 简单的语言特性&#xff0c;在云计算、Web 开发、自动化运维、数据科…

Spark / Java - atomic.LongAccumulator 与 Spark.util.LongAccumulator 计数使用

目录 一.引言 二.atomic.LongAccumulator 1.构造方法 2.使用方法 3.创建并使用 三.Spark.util.LongAccumulator 1.构造方法 2.使用方法 一.引言 使用 Spark 进行大数据分析或相关操作时&#xff0c;经常需要统计某个步骤或多个步骤的相对耗时或数量&#xff0c;java.u…

Java设计模式-适配器模式Adapter

介绍 适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示&#xff0c;主的目的是兼容性&#xff0c;让原本 因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)适配器模式属于结构型模式主要分为三类&#xff1a;类适配器模式、…

树莓派自带的python3.9->python3.7

卸载python3.9&#xff1a;sudo apt-get remove python3卸载之后一些包可以使用sudo apt autoremove这个命令删除卸载成功如果出现问题后续再来更新&#xff08;出现问题后后续安装python也会失败&#xff09;&#xff08;先不要安装先看&#xff09;安装python3.7&#xff1a;…

C语言第30课笔记

1.strerror(errno要包含头文件errno.h) 2.perror头文件为stdio.h 3.一些字符函数 4.字母大小写转换函数 5.memmove理论上是memcpy的升级版(可以自己拷贝自己)。 6.匿名结构体类型在类型创建好了之后直接创建变量&#xff0c;只能用一次。两个完全相同的匿名结构体类型&#xf…

【八】Netty HTTP协议--文件服务系统开发

Netty HTTP协议--文件服务系统开发介绍HTTP-文件系统场景描述流程图代码展示netty依赖服务端启动类 HttpFileServer服务端业务逻辑处理类 HttpFileServerHandler结果展示错误路径文件夹路径文件路径遗留bugbug版本总结介绍 由于Netty天生是异步事件驱动的架构&#xff0c;因此…

java EE初阶 — Synchronized 的原理

文章目录1. Synchronized 的优化操作1.1 偏向锁1.2 轻量级锁&#xff08;自旋锁&#xff09;1.3 重量级锁2. 其他的优化操作2.1 锁消除2.2 锁粗化3. 相关面试题1. Synchronized 的优化操作 两个线程针对同一个对象加锁&#xff0c;就会产生阻塞等待。 Synchronized 内部其实还有…

ubuntu docker elasticsearch kibana安装部署

ubuntu docker elasticsearch 安装部署 所有操作尽量在root下操作. 安装docker 1. 由于是基于宝塔面板安装的所以简答的点击操作即可完成安装. 我这里已经是正常的安装好了. 2.dcoker 镜像加速 https://cr.console.aliyun.com/cn-hangzhou/instances访问这个网址进去进行了…

快速上手Golang

自动推导赋值:自动推导赋值Go中 不同的数据类型不能进行计算对于浮点型默认都是float64 精确到小数点后15位单引号的 为字节类型 一位0~255的字符转换双引号的 为字符串类型多重赋值多重赋值a,b:1,2格式输出格式输出printf“%3d”三位整数&#xff0c;不满足三位时头部补空格“…

录制课程用什么软件好?3款超好用的课程视频录课软件

在互联网技术的飞速发展下&#xff0c;在线教学已经成为一种新型的教学形式&#xff0c;与传统的教学方法相比&#xff0c;在线教学具有低成本、突破地域、时间灵活、形式多样的教学方式。那录制课程用什么软件好&#xff1f;今天小编就跟大家分享3款超好用的课程视频录课&…

认真研究MySQL的主从复制(一)

【1】主从复制概述 ① 如何提升数据库并发能力 在实际工作中&#xff0c;我们常常将Redis作为缓存与MySQL配合使用&#xff0c;当有请求的时候&#xff0c;首先会从缓存中进行查找。如果存在就直接取出&#xff0c;如果不存在再访问数据库。这样就提升了读取的效率&#xff0…

中国数据库的诸神之战

作者 | 唐小引出品 | 《新程序员》编辑部“现在的数据库产品实在是太多了&#xff01;”前几天&#xff0c;我和深耕数据库/大数据近 30 年的卢东明老师相聊时&#xff0c;他发出了这样的感慨。将包括 DB-Engines Ranking 以及国内数据库排行等在内的数据库产品列表进行汇总&am…

快速入门Freemarker模块引擎技术

1、 freemarker 介绍 ​ FreeMarker 是一款 模板引擎&#xff1a; 即一种基于模板和要改变的数据&#xff0c; 并用来生成输出文本(HTML网页&#xff0c;电子邮件&#xff0c;配置文件&#xff0c;源代码等)的通用工具。 它不是面向最终用户的&#xff0c;而是一个Java类库&am…

采场的车辆管理及卸料点计数管理有哪些难题需要解决

近期&#xff0c;安环部检查采矿区域工程车辆驾驶人员情况时&#xff0c;发现有部分驾驶员及工作人员存在违规顶替情况&#xff0c;有非注册备案人员驾驶矿用工程车辆违规作业。为了进行统一有效的人员车辆管理&#xff0c;同时能监督安全员定期对采矿作业区进行安全巡查&#…

Camtasia Studio2023喀秋莎新增功能及电脑配置要求介绍

Camtasia Studio2023具有强大的视频播放和视频编辑功能&#xff0c;录制屏幕后&#xff0c;根据时间轴对视频剪辑进行各种标记、媒体库、画中画、画中画、画外音当然&#xff0c;也可以导入现有视频并对其进行编辑操作。编辑完成后&#xff0c;可以将录制的视频输出为最终的视频…