[C++] 初接触 泛型编程—— C++ 模板分析

news/2024/4/27 21:45:04/文章来源:https://blog.csdn.net/dxyt2002/article/details/127522772

image-20221025220158107


泛型编程

C++中引入了重载的概念,使得可以编写多个函数名相同但参数、返回值不同的函数,例如:

image-20220630125804097

相同的函数名可以传入不同的参宿,进而调用不同的函数

但,即使有了重载,相同功能的函数 还要分别对不同的类型进行编写,未免过于繁琐

有没有一种方法,对于相同的功能,只需要定义一个类似 浇筑用的模具,让编译器 按照模具根据不同的类型 自动将函数 "铸造" 出来呢?

有!这就需要提到泛型编程了.

泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。C++中模板是泛型编程的基础。

模板

C++中的的模板,分为两部分:

  1. 函数模板
  2. 类模板

本篇文章就从函数模板开始讲起

函数模板

函数模板代表了一种函数,而不是某个函数。

该函数模板与类型无关,只有在被使用的时候才会根据使用时实参的类型,产生该种函数特定类型的版本
类似于浇筑,铜水浇筑 得到铜制品,铝水浇筑 得到铝制品

函数模板的结构

函数模板跟普通的函数定义没有很大的差别,还以swap函数为例,它的模板应该是:

image-20220630134203858

即,函数模板的格式应该是:

template<typename T1, typename T2, ......, typename Tn>
函数返回值 函数名(参数列表)
{}

其中 T1T2Tn等,都可以随意起名,其实就是表示 未知类型,且一个名字只能表示一个类型

而函数的内容及参数,都使用未知类型定义或声明就可以

而 模板只是模板,未使用时编译器不会实例化

函数模板的原理及实例化

函数模板是为编译器提供的,编译器会在编译阶段,根据函数模板被使用时 实参的类型 来推断生成特定类型的函数供其调用

举个栗子

还以swap函数为例

image-20220630140832260 image-20220630140920692

这个就是函数模板大致的使用过程 及 结果

根据反汇编代码可以看到,编译器是会对使用的函数模板进行实例化的,并且 函数调用 调用的不是模板,而是有模板实例化出的函数

上面的例子是让编译器对实参类型进行推断,进而实例化生成特定类型的函数,这种方法叫:
隐式实例化:让编译器根据实参推演模板参数的实际类型

而,函数模板实例化函数还有另外一种方法:
显式实例化:在函数名后的<>中指定模板参数的实际类型


一般当编译器无法根据实参推演模板参数的实际类型时,就必须手动显式实例化

举个栗子

对于下面这种函数模板:

image-20220630143145311

模板参数只用在了返回值类型上,而函数的参数的类型指定为 int
这样编译器是无法根据实参类型,进行推断的:
image-20220630143937841

这时候,就必须使用显式实例化指定类型了:
image-20220630144116496

显式实例化使用函数模板的格式就是:

在函数名和参数列表之间 + <类型>函数名<类型>(参数)

当然,不只是无法隐式实例化才能使用显式实例化的,任何函数模板都可以显式实例化
image-20220630151022809
即使编译器可以推断出 模板参数的类型,也同样可以使用 显式实例化

关于隐式实例化,除了上面正常使用的例子之外,还有一些需要注意的地方:

如果对只有一个模板参数的函数模板,传入两个不同类型的参数会发生什么?

image-20220630150013137

发生这种情况,解决方法有两种:强制类型转换显式实例化,即:
image-20220630150407051
但是由于这两种方式都发生了类型的转换,所以 对应的函数模板的参数也需要改变为 const 修饰的:
image-20220630150541615

PS:模板参数可以给缺省类型,模板函数的参数也可以给缺省类型

模板参数的匹配原则

如果存在 一个非模板函数 与 函数模板同名

  1. 在使用该函数时,如果实参类型与非模板函数匹配,则优先调用非模板函数

    no_template 可以看到,当实参类型与非模板函数匹配时,优先调用非模板函数

    当然也可以使用 显式实例化 强制走函数模板:
    no_template&template

  2. 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。上面已经证实了
    但是,如果模板可以产生一个具有更好匹配的函数, 那么将选择函数模板
    better_template
    此例中,模板参数使用了两个,所以参数传入两个类型不报错,且调用模板参数实例化函数

  3. 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换

类模板

类模板的结构

template<class T1, class T2, ......, class Tn>
class 类模板名
{//类模板成员
}

而类模板中成员的定义 就与 函数模板的定义一样

举个栗子 image-20220630162812338

类模板中,成员函数在类外定义时:

  1. 需要加上模板参数列表
  2. 需要在成员函数前标明类域

类模板的实例化

与 函数模板的函数实例化不同,类模板 类的实例化必须显式实例化,不能推演

即:
image-20220630163556763

以上面的类模板为例,这也说明了,类模板实例化类时,SeqList<类型> 才是类名,而SeqList不是类名

模板 定义与声明 无法分离在两个文件中

模板 定义与声明 无法分离在两个文件中,意思就是 模板的声明和定义必须统一放在头文件中。否则会发生链接错误

为什么?

这个原因就要从编译链接的角度分析了

关于编译链接,可阅读博主文章:

首先要知道,编译链接的过程大致分为:预处理编译汇编链接

前三个过程中,多个源文件之间是没有联系的。

即,假如 模板的定义和声明分离在了两个文件中,也就是这样:
20220630164906

那么源文件经过预处理之后就会大致变成这样,且文件名会变为.i 后缀:
image-20220630165723271

可以非常明显的发现,main 函数所在文件里是没有关于类模板函数的定义

并且 直到 汇编操作之后,链接之前 ,这两个文件之间是没有任何联系的
即, mian.i 不能向 template.i 寻找内容,template.i 也不能向 mian.i 寻找内容

这就导致了

  1. template.i 文件中,没有需要类模板实例化的操作,所以不会进行类模板实例化
  2. mian.i 文件中,有需要类模板实例化的操作SeqList<int> slt;,但是此文件的类模板中 成员函数没有定义,只有声明,所一直会生成相应的类的成员函数的符号,没有实质成员函数的地址

进而会导致,最后的链接操作,两文件中都没有关于函数的地址,所以 只有函数名没有函数地址,会导致链接错误
image-20220630172000157

所以,模板的定义与声明 最好不要分离到两个文件中,如果非要分离在两个文件中,那就需要:

对需要实例化的模板,在模板定义位置显式实例化
image-20220630172403326

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

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

相关文章

Python之numpy数组篇(下)

目录 一、数组排序 1、概念 2、升序&#xff0c;最大、最小值 3、原地、横向排序 二、数组内积运算 1、概念 2、代码例子 三、访问数组元素 1、使用介绍 2、行列直接访问 3、切片 4、行列访问扩展 四、数组对函数运算的支持 1、概念 2、例子 五、改变数组形状 1…

1.3.3系统调用

文章目录为什么引入系统调用什么是系统调用系统调用和库函数的区别系统调用的背后为什么引入系统调用 为了防止这样情况的发生&#xff0c;就是防止进程能够随意的去调用我们的系统资源&#xff0c;操作系统提供了系统调用的功能&#xff0c;用户进程想要使用打印机这种共享资源…

12_Vue事件总结

事件总结 事件修饰符连携 准备工作 html <!-- 定义一个容器 --><div class="app"><!-- 事件修饰符连携 --><div class="box" @click="toBaidu"><a href="https://www.baidu.com" @click.stop="toBaid…

Java代码审计前置知识——SpringMVC基础

目录 (一&#xff09;回顾MVC 1.1 什么是MVC Model&#xff08;模型&#xff09; View&#xff08;视图&#xff09; Controller&#xff08;控制器&#xff09; 1.2 Model1时代 1.3 Model2时代 总结 1.4 回顾Servlet 0x01 新建一个Maven工程当做父工程,pom依赖 0x0…

1.1.2操作系统的特征

操作系统是一个系统软件&#xff0c;但与其他系统软件和应用软件有很大的不同&#xff0c;就是它拥有自己的特殊性&#xff0c;及基本特征 首先共享和并发是相互存在的条件共享和并发是虚拟和异步的前提&#xff0c;是操作系统的两个最基本的特征 1并发 拿餐厅吃饭举例子&…

3.3.3JavaScript网页编程——WebAPI(JS之BOM含正则)

目录BOMwindow对象定时器-延时函数setTimeoutJS执行机制&#xff08;执行栈、任务队列&#xff09;面试要问location对象location.href (获取完整url或者赋值)location.search (获取?后面的)location.hash(获取#号后面的)location.reloadnavigator对象&#xff08;检测浏览器移…

10_事件处理阶段

v-on指令 语法 v-on:xxx 这里的xxx指代的是各类事件类型,例如单击,双击,鼠标悬停,键盘监听等等...... 准备工作 准备一个容器,两个按钮,一个按钮不传递参数,另一个按钮传递参数 <body><!-- 创建一个容器 --><div class="subject"><!-- 标…

having where的区别,SQL70 返回更多的产品

返回更多的产品_牛客题霸_牛客网 (nowcoder.com)https://www.nowcoder.com/practice/dc91b7d2de3c4603a55995e83210f605?tpId298&tqId2368029&ru/exam/oj&qru/ta/sql-teach-yourself/question-ranking&sourceUrl%2Fexam%2Foj%3Fpage%3D1%26tab%3DSQL%25E7%25A…

MMSegmentation V0.27.0训练与推理自己的数据集(二)

1、官方模型转换MMSegmentation风格 如果你想自己转换关键字使用官方存储库的预训练模型&#xff0c;我们还提供了一个脚本swin2mmseg.py在tools directory &#xff0c;将模型的关键字从官方的repo转换为MMSegmentation风格。 python tools/model_converters/swin2mmseg.py …

一篇文章带你了解服务器操作系统——Linux简单入门

一篇文章带你了解服务器操作系统——Linux简单入门 Linux作为服务器的常用操作系统,身为工作人员自然是要有所了解的 在本篇中我们会简单介绍Linux的特点,安装,相关指令使用以及内部程序的安装等本篇内容属于《瑞吉外卖》的知识科普部分,有兴趣可以查看一下《瑞吉外卖》的相…

欧拉路径(欧拉环游、欧拉回路)

一个流行的游戏是用铅笔画这些图&#xff0c;但是图中的每一条边都只能被画一次&#xff0c;在画图过程中铅笔不能离开纸面。难度更高的问题是&#xff0c;不光要一笔画完图&#xff0c;并且起点和终点还要落在同一处。如果我们将上面的三个图形都看作图数据结构&#xff0c;那…

flash动画设计并发布、嵌入到网页

【创意内容】 Flash动画设计,二维动画自己选择了动画主题,有三个板块:bubbles动画、蝴蝶飞动画、全球游线图动画,都是自己做的,使用了场景运用动画、图片的滚动、形状遮罩等功能。 【程序运行截图】 bubbles butterflies global

ICCV 2021 | Y-Net:轨迹-场景信息的真正融合

今天没有多余的解释&#xff0c;直接开始吧~ 1. Y-Net网络结构 Y-Net的网络结构长什么样子呢&#xff1f;Y-Net的网络结构就长下图这样子。看上去我好像在自言自语&#xff0c;其实你仔细揣摩就会发现&#xff0c;我真的是在自言自语。可以看到说&#xff0c;Y-Net网络输入的是…

TPH-YOLOv5: 基于Transformer预测头的改进YOLOv5用于无人机捕获场景目标检测

代码链接&#xff1a;GitHub - cv516Buaa/tph-yolov5 这是一篇针对无人机小目标算法比赛后写的论文&#xff0c;无人机捕获场景下的目标检测是近年来的热门课题。由于无人机总是在不同的高度上飞行&#xff0c;目标尺度变化剧烈&#xff0c;给网络优化带来了负担。此外&#xf…

buu [NPUCTF2020]认清形势,建立信心

题目&#xff1a; from Crypto.Util.number import * from gmpy2 import * from secret import flagp getPrime(25) e # Hidden q getPrime(25) n p * q m bytes_to_long(flag.strip(b"npuctf{").strip(b"}"))c pow(m, e, n) print(c) print(pow(2,…

hadoop至MapReduce-004

MapReduce定义 MapReduce是一个分布式运算程序的编程框架&#xff0c;核心功能是将用户编写的业务逻辑代码和自带默认组件组合成一个完整的分布式运算程序&#xff0c;并发运行在hadoop集群上 MapReduce的优缺点 优点 易于编程&#xff1a;用户只关心业务逻辑代码扩展性&am…

webpack 异步import生成代码解析

文章目录原文件内容文件目录打包前打包后入口文件生成代码生成的一些辅助方法__webpack_require__.m__webpack_require__.d__webpack_require__.o__webpack_require__.u__webpack_require__.g__webpack_require__.r导入文件通用方法__webpack_require__异步文件引入获取下载文件…

AntDB-M设计之CheckPoint

1.引 言 数据库服务能力提升是一项系统性的工程&#xff0c;在不同的应用场景下&#xff0c;用户对于数据库各项能力的关注点也不同&#xff0c;如&#xff1a;读写延迟、吞吐量、扩展性、可靠性、可用性等等。国内不少数据库系统通过系统架构优化、硬件设备升级等方式&…

教程:使用Jmeter对带token的接口进行压测

最近在研究并发&#xff0c;用到了Jmeter对接口进行压力测试&#xff0c;记录下使用过程 一. 配置/bin下的Jmeter.properties&#xff0c;打开以下两项配置&#xff0c;一个是默认的编码&#xff0c;一个是默认的语言 二. 打开jmeter.bat运行&#xff0c;新建线程组&#xff0…

qt学习笔记6:ui实例 登录窗口布局

首先从ui布局界面去进行大致布局&#xff0c; 可以先把默认的一些移除掉&#xff0c;变成一个大的空窗口 用户窗口&#xff0c;一般都得有一个用户名和密码&#xff08;用label&#xff09;输入用Line edit&#xff0c; 再来俩按钮pushButton&#xff0c; 但仅仅这样是没有意义…