【C++11新特性】可变参数模板

news2024/9/24 15:28:55

文章目录

  • 1. 认识可变参数模板
  • 2. 可变参数模板的定义方式
  • 3. 参数包的展开方式
    • 3.1 递归展开参数包
    • 3.2 逗号表达式展开参数包

1. 认识可变参数模板

可变参数模板是C++11新增的最强大的特性之一,它对参数高度泛化,能够让我们创建可以接收可变参数的函数模板和类模板。

  • 在C++11之前,类模型和函数模型中只能包含固定数量的模板参数,可变参数模板无疑是一个巨大的改进,但由于可变参数模板比较抽象,因此使用起来需要一定技巧。
  • 在C++11之前其实也有可变参数的概念,比如printf函数就能够接收任意多个参数,但这是函数参数的可变参数,并不是模板的可变参数。

2. 可变参数模板的定义方式

在这里插入图片描述

  • 模板参数Args前面有省略号,代表它是一个可变参数模板,我们把带省略号的参数称为参数包,参数包里面可以包含0到N(N>=0)个模板参数,而args则是一个函数形参参数包。
  • 模板参数包Args和函数形参参数包args的名字可以任意指定。

现在调用ShowList函数时就可以传入任意多个参数了,并且这些参数可以是不同类型的。
在这里插入图片描述
我们可以在函数模板中通过sizeof计算参数包中参数的个数
在这里插入图片描述
但是我们无法直接获取参数包中的每个参数,只能通过展开参数包的方式获取,这是使用可变参数模板的一个主要特点,也是最大的难点。

3. 参数包的展开方式

3.1 递归展开参数包

递归展开参数包的方式如下:

  • 给函数模板增加一个模板参数,这样就可以从接收到的参数包分离出一个参数出来。
  • 在函数模板中递归调用该函数模板,调用时传入剩下的参数包。
  • 如此递归下去,每次分离出参数包的一个参数,直到参数包中的所有参数都被取出来。

比如我们要打印调用函数时传入的各个参数,那么函数模板可以这样编写:
在这里插入图片描述
这时我们面临的问题就是,如果终止函数的递归调用。

编写无参的递归终止函数

我们可以在刚才的基础上,再编写一个无参的递归终止函数,该函数的函数名与展开函数的函数名相同。
在这里插入图片描述
这样一来,当递归调用ShowList函数模板时,如果传入的参数包中参数的个数为0,那么就会匹配到这个无参的递归终止函数,这样就结束了递归。

  • 但如果外部调用ShowList函数时就没有传入参数,那么就会直接匹配到无参的递归终止函数。
  • 而我们本意是想让外部调用ShowList函数时匹配的都是函数模板,并不是让外部调用时直接匹配到这个递归终止函数。

鉴于此,我们可以将展开函数和递归调用函数的函数名改为ShowListArg,然后重新编写一个ShowList函数模板,该函数模板的函数体重要做的技术调用ShowListArg函数展开参数包。
在这里插入图片描述
这时无论外部调用时传入多少个参数,最终匹配到的都是同一个函数了。

编写带参的递归终止函数

除了编写无参的递归终止函数,也可以编写带参的递归终止函数来终止递归,比如这里编写一个带一个参数的递归终止函数。
在这里插入图片描述
这样一来,在递归调用过程中,如果传入的参数包中参数的个数为1,那么就会匹配到这个递归终止函数,这样也就结束了递归。但是需要注意,这里的递归调用函数需要写成函数模板,因为我们并不知道最后一个参数是什么类型的。

但该方法有一个弊端就是,我们在调用ShowList函数时必须至少传入一个参数,否则就会报错。因为此时无论是调用递归终止函数还是展开函数,都需要至少传入一个参数。

不可以通过判断参数包中参数的个数来终止递归!

既然我们可以通过sizeof计算出参数包中参数的个数,那我们能不能在ShowList函数中设置一个判断,当参数包中参数为0时就终止递归呢?

这种方法是不可行的,原因如下:

  • 函数模板并不能调用,函数模板需要在编译时根据传入的实参类型进行推演,生成对应的函数,这个生成的函数才能够被调用。
  • 而这个推演过程是在编译时进行的,当推演到参数包args中参数个数为0时,还需要将当前函数推演完毕,这时就会继续推演传入0个参数时的ShowList函数,此时就会产生报错,因为ShowList函数至少要求传入一个参数。

3.2 逗号表达式展开参数包

通过列表获取参数包中的参数

数组可以通过列表进行初始化
在这里插入图片描述
如果参数包中各个参数的类型都是整型,那么也可以把这个参数包放到列表当初始化这个整形数组,此时参数包中参数就放到数组中了。
在这里插入图片描述这时调用ShowList函数时就可以传入多个整型参数了。

在这里插入图片描述

但C++并不像Python这样的语言,C++规定一个容器中存储的数据类型必须是相同的,因此如果这样写的话,那么调用ShowList函数时传入的参数只能是整型的,并且还不能传入0个参数,因为数组的大小不能为0,因此我们还需要在此基础上借助逗号表达式来展开参数包。

通过逗号表达式展开参数包

虽然我们不能使用不同类型的参数去初始化一个整形数组,但我们可以借助逗号表达式。

  • 逗号表达式会从左到右依次计算各个表达式,并且将最后一个表达式的值作为返回值进行返回。
  • 将逗号表达式的最后一个表达式设置为一个整型值,确保逗号表达式返回的是一个整形值。
  • 将处理参数包中参数的动作封装成一个函数,将该函数的调用动作作为逗号表达式的第一个表达式。

这样一来,在执行逗号表达式时就会先调用处理函数处理对应的参数,然后再将逗号表达式中的最后一个整形值作为返回值来初始化整型数组。
在这里插入图片描述

  • 我们这里要做的就是打印参数包中的各个参数,因此处理函数中要做的就是将传入的参数进行打印即可。
  • 可变参数的省略号在逗号表达式外面,表示需要将逗号表达式展开,如果省略号加在args的后面,那么参数包展开后会全部传入PrintArg函数,代码中的 {(PrintArg(args), 0)…} 将会展开成 {(PrintArg(arg1), 0), (PrintArg(arg2), 0), (PrintArg(arg3), 0), etc…}

这时调用ShowList函数时就可以传入多个不同类型的参数了,但调用时仍然不能传入0个参数,因为数组的大小不能为0,如果想要支持传入0个参数,也可以写一个无参的ShowList函数。

在这里插入图片描述

实际上我们也可以不用逗号表达式,因为这里的问题就是初始化整型数组时必须用整数,那我们可以将处理函数的返回值设置为整型,然后用这个返回值去初始化整型数组也是可以的。
在这里插入图片描述

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

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

相关文章

【SpringBoot学习笔记02】静态资源

Spring Boot 通过 MVC 的自动配置类 WebMvcAutoConfiguration 为这些 WebJars 前端资源提供了默认映射规则,部分源码如下。 jar包: JAR 文件就是 Java Archive File,顾名思意,它的应用是与 Java 息息相关的,是 Java 的…

springboot整合rabbitmq死信队列

springboot整合rabbitmq死信队列 什么是死信 说道死信,可能大部分观众大姥爷会有懵逼的想法,什么是死信?死信队列,俗称DLX,翻译过来的名称为Dead Letter Exchange 死信交换机。当消息限定时间内未被消费,…

编码过程中需要注意哪些安全问题?

SQL 安全 注入式(Inject)攻击是一类非常常见的攻击方式,其基本特征是程序允许攻击者将不可信的动态内容注入到程序中,并将其执行,这就可能完全改变最初预计的执行过程,产生恶意效果。下面是几种主要的注入…

为 Python 创建别名

有时您有自己喜欢的 Python 版本,并且不想在新版本到来时放弃它。 您的旧脚本可能无法在新版本的 Python 上运行,或者旧版本上的项目太多,将它们迁移到新版本是一场马拉松。 在这种情况下,您决定保留两个版本的 Python。 在本文中…

什么是算法?

目录 算法是指解决方案的准确而完整的描述。 1.算法的基本特征 所谓算法,是一组严谨地定义运算顺序的规则 并且每一个规则都是有效的,且是明确的 此顺序将在有限的次数下终止 什么是算法? 算法的4个基本特征 算法的6个基本方法 选择算…

浏览器输入一个URL之后发生了什么?

URL解析DNS解析TCP连接TSL连接HTTP请求TCP挥手接收并解析响应 URL 解析 主要分为: 协议,eg http,https域名或者ip地址,eg www.baidu.com 域名相对于ip地址来说,更方便人们记忆,但是实际的网络传输中使用的是ip地址 端…

Java“牵手”天猫商品快递费用API接口数据,天猫API接口申请指南

天猫平台商品快递费用接口是开放平台提供的一种API接口,通过调用API接口,开发者可以获取天猫商品的标题、价格、库存、商品快递费用,宝贝ID,发货地,区域ID,快递费用,月销量、总销量、库存、详情…

十几款拿来就能用的炫酷表白代码

「作者主页」:士别三日wyx 「作者简介」:CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」:小白零基础《Python入门到精通》 表白代码 1、坐我女朋友好吗,不同意就关机.vbs2、坐我女朋友好吗&…

订单状态定时处理、来单提醒和客户催单

一、Spring Task 1.1 基本介绍 1.2 cron表达式 cron表达式其实就是一个字符串,通过cron表达式可以定义任务触发的时间 构成规则:分为6或7个域,由空格分隔开,每个域代表一个含义 每个域的含义分别为:秒、分钟、小时、日…

C++|观察者模式

观察者模式: 定义对象间的一种一对多(变化)的依赖关系,以便当一个 对象(Subject)的状态发生改变时,所有依赖于它的对象都 得到通知并自动更新 动机: 在软件构建过程中,我们需要为某些对象建立…

【深度学习 | 核心概念】那些深度学习路上必经的核心概念,确定不来看看?(一)

🤵‍♂️ 个人主页: AI_magician 📡主页地址: 作者简介:CSDN内容合伙人,全栈领域优质创作者。 👨‍💻景愿:旨在于能和更多的热爱计算机的伙伴一起成长!!&…

基于吉萨金字塔建造算法优化的BP神经网络(预测应用) - 附代码

基于吉萨金字塔建造算法优化的BP神经网络(预测应用) - 附代码 文章目录 基于吉萨金字塔建造算法优化的BP神经网络(预测应用) - 附代码1.数据介绍2.吉萨金字塔建造优化BP神经网络2.1 BP神经网络参数设置2.2 吉萨金字塔建造算法应用…

SpringBoot与前端交互遇到的一些问题

一、XXX.jar中没有主清单属性 场景: SpringBoot打的jar包在Linux运行报错 解决方案: 百度找了很多都是一样的答案,但是解决不了我的问题,于是我新建了一个springboot项目发现打的jar包可以在Linux上运行。检查了下只要把下面这2个…

R语言之缺失值处理

文章和代码已经归档至【Github仓库:https://github.com/timerring/dive-into-AI 】或者公众号【AIShareLab】回复 R语言 也可获取。 文章目录 缺失值处理1. 识别缺失值2. 探索数据框里的缺失值3. 填充缺失值3.1 删除缺失值:na.omit( )、complete.cases( …

机器学习:异常检测实战

文章目录 Anomaly Detection目录任务介绍数据集方法评估Baseline报告报告评价标准 Anomaly Detection 目录 任务介绍 无监督的异常检测 数据集 方法 autoencode 是否能够还原出原始类型图片,基于重构loss来判断是否正常 重构误差当作异常分数 评估 采用ROC和AUC…

Kubernetes入门 十、HPA 自动扩/缩容

目录 概述安装metrics-server使用HPA 概述 我们已经可以通过手动执行 kubectl scale 命令实现Pod的扩缩容,但是这显然不符合 Kubernetes 的定位目标–自动化和智能化。Kubernetes 期望可以通过监测Pod的使用情况,实现 Pod 数量的自动调整,于…

Orange Pi 3B 开发板 开箱评测 和 系统安装教程

香橙派 Orange Pi 3B(RK3566)开发板 开箱测评 和 系统烧录教程 简介 香橙派 Orange Pi 3B 是一款树莓派大小的单板计算机,但接口更加齐全,包括一个全尺寸 HDMI 接口和一个 M.2 存储插槽,售价199起。 Orange Pi 3B 采…

【Tkinter系列02/5】界面初步和布局

本文是系列文章第二部分。前文见:【Tkinter系列01/5】界面初步和布局_无水先生的博客-CSDN博客 说明 一般来说,界面开发中,如果不是大型的软件,就不必用QT之类的实现,用Tkinter已经足够,然而即便是Tkinter规…

软考:中级软件设计师:OSI/RM七层模型,网络技术标准与协议

软考:中级软件设计师:OSI/RM七层模型 提示:系列被面试官问的问题,我自己当时不会,所以下来自己复盘一下,认真学习和总结,以应对未来更多的可能性 关于互联网大厂的笔试面试,都是需要细心准备的…

No117.精选前端面试题,享受每天的挑战和学习

文章目录 断点续传怎么做的秒传怎么实现var let const 块级作用域ts Partial Omit 怎么实现的箭头函数有哪些限制箭头函数为什么不能作为构造函数promise常用apiMap和Object的区别vue怎么实现双向绑定 断点续传怎么做的 断点续传是指在文件下载或上传过程中,当连接…