C++模板(详解)

news2025/1/19 23:21:55

非类型模板参数

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

例如,我们要实现一个静态数组的类,就需要用到非类型模板参数。

template<class T, size_t N> //N:非类型模板参数
class Array
{
public:
	size_t size()
	{
		return N;
	}
private:
	T _array[N]; //利用非类型模板参数指定静态数组的大小
};

使用非类型模板参数后,我们就可以在实例化对象的时候指定所要创建的静态数组的大小了。

int main()
{
	Array<int, 10> a1; //定义一个大小为10的静态数组
	cout << a1.size() << endl; //10
	Array<int, 100> a2; //定义一个大小为100的静态数组
	cout << a2.size() << endl; //100
	return 0;
}

注意:

  1. 非类型模板参数只允许使用整型家族,浮点数、类对象以及字符串是不允许作为非类型模板参数的。
  2. 非类型的模板参数在编译期就需要确认结果,因为编译器在编译阶段就需要根据传入的非类型模板参数生成对应的类或函数。

模板的特化

概念

这里举一个简单的例子来说明什么是特化,下面是用于比较两个任意相同类型的数据是否相等的函数模板。

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

我们大概会这样使用该函数模板:

cout << IsValue(1, 1) << endl; //1
cout << IsValue(1.1, 2.2) << endl; //0

这样使用是没有问题的,它的判断结果也是我们所预期的,但是我们也可能会这样去使用该函数模板:

char a1[] = "2023CSDN!";
char a2[] = "2023CSDN!";
cout << IsValue(a1, a2) << endl; //0

判断结果是这两个字符串不相等,这很好理解,因为我们希望的是该函数能够判断两个字符串的内容是否相等,而该函数实际上判断是确实这两个字符串所存储的地址是否相同,这是两个存在于栈区的字符串,其地址显然是不同的。
类似于上述实例,使用模板可以实现一些与类型无关的代码,但对于一些特殊的类型可能会得到一些错误的结果,此时就需要对模板进行特化,即在原模板的基础上,针对特殊类型进行特殊化的实现方式
 

函数模板特化

对于上述实例,我们知道当传入的类型是char*时,应该依次比较各个字符的ASCII码值进而判断两个字符串是否相等,或是直接调用strcmp函数进行字符串比较,那么此时我们就可以对char*类型进行特殊化的实现。

函数模板的特化步骤:

  1. 首先必须要有一个基础的函数模板。
  2. 关键字template后面接一对空的尖括号<>。
  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型。
  4. 函数形参表必须要和模板函数的基础参数类型完全相同,否则不同的编译器可能会报一些奇怪的错误。

对于上述实例char*类型的特化如下:

//基础的函数模板
template<class T>
bool IsValue(T x, T y)
{
	return x == y;
}
//对于char*类型的特化
template<>
bool IsValue<char*>(char* x, char* y)
{
	return strcmp(x, y) == 0;
}

注意: 一般情况下,如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出。例如,上述实例char*类型的特化还可以这样给出:

//基础的函数模板
template<class T>
bool IsValue(T x, T y)
{
	return x == y;
}
//对于char*类型的特化
bool IsValue(char* x, char* y)
{
	return strcmp(x, y) == 0;
}

类模板
特化

不仅函数模板可以进行特化,类模板也可以针对特殊类型进行特殊化实现,并且类模板的特化又可分为全特化和偏特化(半特化)。

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

例如,对于以下类模板:

template<class T1, class T2>
class A
{
public:
	//构造函数
	A()
	{
		cout << "A<T1, T2>" << endl;
	}
private:
	T1 _t1;
	T2 _t2;
};

当T1和T2分别是double和int时,我们若是想对实例化的类进行特殊化处理,那么我们就可以对T1和T2分别是double和int时的模板进行特化。

函数模板的特化步骤:

  1. 首先必须要有一个基础的类模板。
  2. 关键字template后面接一对空的尖括号<>。
  3. 类名后跟一对尖括号,尖括号中指定需要特化的类型。

对于T1是double,T2是int的特化如下:

//对于T1是double,T2是int时进行特化
template<>
class A<double, int>
{
public:
	//构造函数
	A()
	{
		cout << "A<double, int>" << endl;
	}
private:
	double _t1;
	int _t2;
};

那么如何证明当T1是double,T2是int时,使用的就是我们自己特化的类模板呢?
当我们实例化一个对象时,编译器会自动调用其默认构造函数,我们若是在构造函数当中打印适当的提示信息,那么当我们实例化对象后,通过观察控制台上打印的结果,即可确定实例化该对象时调用的是不是我们自己特化的类模板了。

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

例如,对于以下类模板:

template<class T1, class T2>
class A
{
public:
	//构造函数
	A()
	{
		cout << "A<T1, T2>" << endl;
	}
private:
	T1 _t1;
	T2 _t2;
};

偏特化又可分为以下两种表现形式:

1、部分特化
我们可以仅对模板参数列表中的部分参数进行确定化。
例如,我们可以对T1为int类型的类进行特殊化处理。

//对T1为int的类进行特化
template<class T>
class A<int, T>
{
public:
	//构造函数
	A()
	{
		cout << "A<int, T>" << endl;
	}
private:
	int _t1;
	T _t2;
};

此时只要实例化对象时指定T为int,就会使用这个特化的类模板来实例化对象。

 2、参数更进一步的限制
偏特化并不仅仅是指特化部分参数,而是针对模板参数进一步的条件限制所设计出来的一个特化版本。
例如,我们还可以指定当T1和T2为某种类型时,使用我们特殊化的类模板。

//两个参数偏特化为指针类型
template<class T1, class T2>
class A<T1*, T2*>
{
public:
	//构造函数
	A()
	{
		cout << "A<T1*, T2*>" << endl;
	}
private:
	T1 _t1;
	T2 _t2;
};
//两个参数偏特化为引用类型
template<class T1, class T2>
class A<T1&, T2&>
{
public:
	//构造函数
	A()
	{
		cout << "A<T1&, T2&>" << endl;
	}
private:
	T1 _t1;
	T2 _t2;
};

此时,当实例化对象的T1和T2同时为指针类型或同时为引用类型时,就会分别调用我们特化的两个类模板。

 

 

模板的分离编译

什么是分离编译

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

模板的分离编译

在分离编译模式下,我们一般创建三个文件,一个头文件用于进行函数声明,一个源文件用于对头文件中声明的函数进行定义,最后一个源文件用于调用头文件当中的函数。
按照此方法,我们若是对一个加法函数模板进行分离编译,其三个文件当中的内容大致如下:

 

 但是使用这三个文件生成可执行文件时,却会在链接阶段产生报错。

 下面我们对其进行分析:
我们都知道,程序要运行起来一般要经历以下四个步骤:

        预处理: 头文件展开、去注释、宏替换、条件编译等。
        编译: 检查代码的规范性、是否有语法错误等,确定代码实际要做的工作,在检查无误后,将代码翻译成汇编语言。
        汇编: 把编译阶段生成的文件转成目标文件。
        链接: 将生成的各个目标文件进行链接,生成可执行文件。

以上代码在预处理阶段需要进行头文件的包含以及去注释操作。

这三个文件经过预处理后实际上就只有两个文件了,若是对应到Linux操作系统当中,此时就生成了 Add.i 和 main.i 文件了。

 

 

预处理后就需要进行编译,虽然在 main.i 当中有调用Add函数的代码,但是在 main.i 里面也有Add函数模板的声明,因此在编译阶段并不会发现任何语法错误,之后便顺利将 Add.i 和 main.i 翻译成了汇编语言,对应到Linux操作系统当中就生成了 Add.s 和 main.s 文件。

之后就到达了汇编阶段,此阶段利用 Add.s 和 main.s 这两个文件分别生成了两个目标文件,对应到Linux操作系统当中就是生成了 Add.o 和 main.o 两个目标文件。

前面的预处理、编译和汇编都没有问题,现在就需要将生成的两个目标文件进行链接操作了,但在链接时发现,在main函数当中调用的两个Add函数实际上并没有被真正定义,主要原因是函数模板并没有生成对应的函数,因为在全过程中都没有实例化过函数模板的模板参数T,所以函数模板根本就不知道该实例化T为何类型的函数。

模板分离编译失败的原因:
在函数模板定义的地方(Add.cpp)没有进行实例化,而在需要实例化函数的地方(main.cpp)没有模板函数的定义,无法进行实例化。

解决方法

解决类似于上述模板分离编译失败的方法有两个,第一个就是在模板定义的位置进行显示实例化。
例如,对于上述代码解决方案如下:

在函数模板定义的地方,对T为int和double类型的函数进行了显示实例化,这样在链接时就不会找不到对应函数的定义了,也就能正确执行代码了。

虽然第一种方法能够解决模板分离编译失败的问题,但是我们这里并不推荐这种方法,因为我们需要用到一个函数模板实例化的函数,就需要自己手动显示实例化一个函数,非常麻烦。

现在就来说说解决该问题的第二个方法,也是我们所推荐的,那就是对于模板来说最好不要进行分离编译,不论是函数模板还是类模板,将模板的声明和定义都放到一个文件当中就行了。

模板总结

优点:

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

缺陷:

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

 

C++模板(详解)内容到此介绍结束了,感谢您的阅读!!!

如果内容对你有帮助的话,记得给我点个赞——做个手有余香的人。感谢大家的支持!!!

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

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

相关文章

字节跳动测试岗面试挂在二面,我复盘总结了失败原因,决定再战一次

先说下我基本情况&#xff0c;本科不是计算机专业&#xff0c;现在是学通信&#xff0c;然后做图像处理&#xff0c;可能面试官看我不是科班出身没有问太多计算机相关的问题&#xff0c;因为第一次找工作&#xff0c;字节的游戏专场又是最早开始的&#xff0c;就投递了&#xf…

hystart++ 出炉

周三早上同事发我一个 rfc 9406 链接&#xff0c;microsoft hystart 标准化了&#xff0c;此前只是一个 draft。 说说我的看法。 信收敛不信测量&#xff0c;tcp 是端到端高熵体&#xff0c;对网络一无所知&#xff0c;过度信任测量则无法消除长尾&#xff0c;且可能劣化 p99…

Metasploit入门教程(非常详细)从零基础入门到精通,看完这一篇就够了!

通过本篇文章&#xff0c;我们将会学习以下内容&#xff1a; 1、在Windows上安装Metasploit 2、在Linux和MacOS上安装Metasploit 3、在Kali Linux中使用 Metasploit 4、升级Kali Linux 5、使用虚拟化软件构建渗透测试实验环境 6、配置SSH连接 7、使用SSH连接Kali 8、配…

【牛客刷题专栏】0x32:JZ45 把数组排成最小的数(C语言编程题)

前言 个人推荐在牛客网刷题(点击可以跳转)&#xff0c;它登陆后会保存刷题记录进度&#xff0c;重新登录时写过的题目代码不会丢失。个人刷题练习系列专栏&#xff1a;个人CSDN牛客刷题专栏。 题目来自&#xff1a;牛客/题库 / 在线编程 / 剑指offer&#xff1a; 目录 前言问…

【算法】LFU及其优化

文章目录 什么是LFU&#xff1f;设计思路代码实现&#xff08;基础版本&#xff09;参考论文代码实现&#xff08;优化版本&#xff09;区别 什么是LFU&#xff1f; LRU及其实现 上文讲解了LRU&#xff0c;他是一个基于最近是否被访问来做缓存淘汰的策略。 那么今天介绍一个新…

Postman的简单使用:

1. Postman 1.1 背景 当前主流的开发模式为&#xff1a;前后端分离开发。 前端人员开发前端工程&#xff0c;后端人员开发后端工程&#xff0c;只需要依据这份接口文档即可。在后端开发过程中每开发完一个功能&#xff0c;就需要对这个功能接口进行测试&#xff0c;由于现在是…

图灵完备游戏:信号计数 解法记录

使用1个全加器 2个半加器完成。这关的思想主旨在于如何把输出4&#xff0c;输出2&#xff0c;输出1的情况统一在一根导线上。 首先用一个全加器来完成输入2-4这三个引脚的计数&#xff0c;因为全加器输出范围二进制是00 - 11&#xff0c;而输入正好有两个引脚数位是2和1&…

linux周六串讲

esc. //粘贴复制上一条命令的参数 cat /etc/resolv.conf //查看DNS地址 route -n //查看网关 hostname //临时修改主机名 hostnamectl set-hostname 名称 //永久修改主机名 ssh root192.168.10.233 //用windows远程的格式&#xff0c;在CMD窗口输入这个命令 …

MYSQL数据库测评及整改

1、查询数据库版本&#xff1a;select version(); 2、查询已安装的插件&#xff1a;show plugins; 3、查询插件安装的位置&#xff1a; show variables like "%plugin_dir%"; 4、查询用户&#xff1a;选择数据库&#xff1a; select host,user,plugin from user; 5、…

Read View 数据快照,在MVCC里是如何工作的?

Read View 数据快照&#xff0c;在MVCC里是如何工作的&#xff1f; Read View 有四个重要的字段&#xff1a; ● m_ids &#xff1a;指的是在创建 Read View 时&#xff0c;当前数据库中「活跃事务」的事务 id 列表&#xff0c;注意是一个列表&#xff0c;“活跃事务”指的就…

Seata术语

1.什么是Seata Seata是一款开源的分布式事务解决方案&#xff0c;致力于在微服务架构下提供高性能和简单易用的分布式事务服务。 官网 2.Seata能干嘛 一个典型的分布式事务过程 分布式事务处理过程的一ID三组件模型&#xff1a; Transaction ID XID 全局唯一的事务ID三组…

WSL2编译安卓8.1源码,直接运行模拟器

目录 WSL2编译安卓8.1源码&#xff0c;直接运行模拟器WSL2下载安装移动到非系统盘下载和编译Android8直接运行emulator修改模拟器配置源码导入Android Studio并进行调试断点调试总结 WSL2编译安卓8.1源码&#xff0c;直接运行模拟器 学习Android Framework开发&#xff0c;需要…

零基础CTF入门指南+工具

ctf入门指南 CTF工具包获取地址&#xff1a;http://www.ctftools.com/ 如何入门&#xff1f;如何组队&#xff1f; capture the flag 夺旗比赛 类型&#xff1a; Web 密码学 pwn 程序的逻辑分析&#xff0c;漏洞利用windows、linux、小型机等 misc 杂项&#xff0c;隐写&…

2023年电工杯选题人数发布

该选题人数&#xff0c;主要基于根据各个平台开赛后24小时各项数据统计&#xff0c;进行评估&#xff08;方法见注释&#xff09;&#xff0c;最终得出2023年认证杯二阶段选题人数&#xff0c;大致为 题号选题人数A97B431 注释&#xff1a;选题人数来源&#xff1a;源自各个平台…

Python案例:获取music榜单数据,保存自己的music库

目录 前言本次知识点:开发环境:代码展示括展小知识尾语 &#x1f49d; 前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 本次知识点: 爬虫基本流程 requests的使用 正则表达式的使用 开发环境: 解释器: python 3.8 编辑器: pycharm 2022.3 专业版 第三方模块使用 r…

如何自学网络安全 才不会成为脚本小子?

前言&#xff1a;我们来看看怎么学才不会成为脚本小子 目录&#xff1a; 一&#xff0c;怎么入门&#xff1f; 1、Web 安全相关概念&#xff08;2 周&#xff09; 了解网络安全相关法律法规 熟悉基本概念&#xff08;SQL 注入、上传、XSS、CSRF、一句话木马等&#xff09;。…

基于three.js实现的点击盒子消除游戏

一.项目背景 大学时期参加了机器人协会&#xff0c;并有幸成为了视觉组组长&#xff0c;所以在新一届社团招新上做了一款趣味小游戏来吸引新生的眼球&#xff0c;让大家知道协会的视觉组。 二.代码展示 <!DOCTYPE html> <html> <head> <style> body…

chatgpt赋能python:Python文件名字替换-优化SEO的必备技巧

Python文件名字替换-优化SEO的必备技巧 作为一名有10年Python编程经验的工程师&#xff0c;我深知文件名字替换在优化搜索引擎排名中占有重要的地位。本文将介绍如何使用Python进行文件名字替换以优化SEO&#xff0c;旨在为广大编程初学者提供有益的参考和指导。 什么是文件名…

零基础如何学习 Web 安全?

Web安全不仅是互联网的核心&#xff0c;而且还是云计算和移动互联网的最佳载体。对于信息安全从业者而言&#xff0c;Web安全是一个非常重要的研究课题之一。 Web应用是指采用B/S架构、通过HTTP/HTTPS协议提供服务的统称。随着互联网的广泛使用&#xff0c;社交网络、聊天工具…

小学妹刚毕业没地方住想来借宿?于是我连夜用Python给她找了个好房子,我真是太机智了

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 事情是这样的&#xff0c;小学妹刚毕业参加工作&#xff0c;人生地不熟的&#xff0c; 因为就在我附近上班&#xff0c;所以想找我借宿。。。 想什么呢&#xff0c;都不给住宿费&#xff0c;想免费住&#xff1f; 于是我用…