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

news2024/12/31 5:26:37

可变参数模板

  • 可变参数模板
    • 1、基本介绍
    • 2、递归函数方式展开参数包
    • 3、逗号表达式展开参数包

可变参数模板

1、基本介绍


C++11的新特性可变参数模板能够让你创建可以接受可变参数的函数模板和类模板,相比C++98/03,类模版和函数模版中只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改进。然而由于可变模版参数比较抽象,使用起来需要一定的技巧,所以这块还是比较晦涩的。本章我们只介绍一些基础的可变参数模板特性。

下面就是一个基本可变参数的函数模板

// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}

在不定参数的模板函数中,可以通过如下方式获得args的参数个数:

sizeof...(args)

上面的参数Args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数包”,它里面包含了**0到N(N>=0)**个模版参数。我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特点,也是最大的难点,即如何展开可变模版参数。

2、递归函数方式展开参数包

// 递归终止函数
template <class T>
void ShowList(const T& t)
{
	cout << t << endl;
}

// 展开函数
template <class T, class ...Args>
void ShowList(T value, Args... args)
{
	cout << value << " ";
	ShowList(args...);
}

int main()
{
	ShowList(1);
	ShowList(1, 'A');
	ShowList(1, 'A', std::string("sort"));
	return 0;
}

在这里插入图片描述

解释

  1. 在执行ShowList(1)时由于存在两个重载的模板函数,在进行函数匹配时ShowList(1)会选择最匹配的ShowList(const T& t),于是执行函数并进行类型推导打印出1

  2. 在执行ShowList(1, ‘A’)时,进行函数匹配时ShowList(1, ‘A’)会选择最匹配的ShowList(T value, Args... args),于是执行函数并进行类型推导先打印出1,此时参数包args里面还有一个参数,执行下一句代码ShowList(args…),于是再次进行函数匹配于是匹配到了ShowList(const T& t),于是执行函数并进行类型推导打印出A

  3. 在执行ShowList(1, ‘A’, std::string(“sort”));时,进行函数匹配时ShowList(1, ‘A’, std::string(“sort”));会选择最匹配的ShowList(T value, Args... args),于是执行函数并进行类型推导先打印出1,此时参数包args里面还有两个参数,执行下一句代码ShowList(args…),于是再次进行函数匹配于是匹配到了ShowList(T value, Args... args),于是执行函数并进行类型推导打印出A,此时参数包args里面还有一个参数,执行下一句代码ShowList(args…),于是再次进行函数匹配于是匹配到了ShowList(const T& t),于是执行函数并进行类型推导打印出sort

总结

  • 可以看出第一个只有一个参数的模板函数是每一个同名的可变参数模板函数最后执行的函数,没有此函数,可变参数模板函数的递归终止条件就不能确定,所以又叫这种函数为递归终止函数

  • 可以看出可变参数模板函数,每次递归都只取出参数包里面的一个参数,经过不断递归,最终将所有的参数解析完成,故又将其称为展开函数

在有些场景下面我们也想要一个可变参数模板函数能接收0个参数,我们上面的写法只能接收最低1个参数,因为我们的递归终止函数调用时就是只有1个参数时,为了支持0个参数我们还可以这样进行编码:

// 递归终止函数
void _ShowList()
{
	cout << endl;
}

// 展开函数
template <class T, class ...Args>
void _ShowList(T value, Args... args)
{
	cout << value << " ";
	_ShowList(args...);
}

模板参数包也可以接收0个参数哦!

同样的为了让可变参数模板对外声明的接口更加统一,我们还可以包装一下展开函数与递归终止函数

// 包装一下展开函数与递归终止函数
template <class... Args>
void ShowList(Args... args)
{
	_ShowList(args...);
}
// 没有包装前可变参数模板的对外声明
template <class T, class ...Args>
void _ShowList(T value, Args... args);

// 包装后可变参数模板的对外声明
template <class... Args>
void ShowList(Args... args)

3、逗号表达式展开参数包


递归函数展开参数包是一种标准做法,也比较好理解,但也有一个缺点,就是必须有一个重载的递归终止函数,即必须有一个同名的终止函数来终止递归,但是这样会让人感觉稍有不便。

// 处理每个参数
template <class T>
void PrintArg(T t)
{
	cout << t << " ";
}

// 展开函数
template <class ...Args>
void ShowList(Args... args)
{
	int arr[] = { (PrintArg(args), 0)... };
	cout << endl;
}

int main()
{
	ShowList(1);
	ShowList(1, 'A');
	ShowList(1, 'A', std::string("sort"));
	return 0;
}

在这里插入图片描述

逗号表达式这种展开参数包的方式,不需要通过递归终止函数,是直接在expand函数体中展开的, PrintArg不是一个递归终止函数,只是一个处理参数包中每一个参数的函数。这种就地展开参数包的方式实现的关键是逗号表达式。我们知道逗号表达式会按顺序执行逗号前面的表达式。

expand函数中的逗号表达式:(printarg(args), 0)...,意味着将参数包进行逐个展开,变为了((printarg(arg1),0),(printarg(arg2),0), (printarg(arg3),0), etc… )。

(printarg(args...), 0) // 这种写法意味着将参数包进行整体传递,不进行展开。

也是按照这个执行顺序,先执行printarg(args),再得到逗号表达式的结果0。同时还用到了C++11的另外一个特性——初始化列表,通过初始化列表来初始化一个变长数组, {(printarg(args), 0)...}将会展开成最终会创建一个元素值都为0的数组int arr[sizeof...(Args)]

由于是逗号表达式,在创建数组的过程中会先执行逗号表达式前面的部分printarg(args)打印出参数,也就是说在构造int数组的过程中就将参数包展开了,这个数组的目的纯粹是为了在数组构造的过程展开参数包。

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

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

相关文章

将 ChatGPT 用于数据科学项目的指南

推荐&#xff1a;使用 NSDT场景编辑器 快速搭建3D应用场景 我们都知道 ChatGPT 的受欢迎程度以及人们如何使用它来提高生产力。但是&#xff0c;如果您是新手&#xff0c;则值得注册ChatGPT免费演示并尝试它所能做的一切。您还应该参加我们的 ChatGPT 简介课程&#xff0c;学习…

文旅品牌为何青睐于3D虚拟数字人定制?

随着web3.0技术的到来&#xff0c;数字人技术快速发展&#xff0c;推动着文旅通过3D虚拟数字人定制&#xff0c;探索数字化营销。数字人结合动捕设备&#xff0c;可以颠覆传统玩法&#xff0c;以数字人内容赋能传统宣传手段&#xff0c;通过动捕设备让数字人与用户交流互动&…

U盾难管理?用U盾专用USB集线器

公司有一堆U盾要插着用&#xff0c;但是一台电脑也才两三个接口&#xff0c;怎么办&#xff1f; 三个字&#xff0c;很简单&#xff0c; 一台U盾专用的USB集线器就能解决。 U盾专用集线器为解决网银U盾连接问题而生。 它有四大好处&#xff01; 集中管理 把所有U盾集中到一…

Java泛型(待补充)

泛型是一种“代码模板”&#xff0c;可以用一套代码套用各种类型。 一、什么是泛型&#xff1f; 泛型就是编写模板代码来适应任意类型&#xff1b;泛型的好处是使用时不必对类型进行强制转换&#xff0c;它通过编译器对类型进行检查&#xff1b;注意泛型的继承关系&#xff1a…

RHCE——十七、文本搜索工具-grep、正则表达式

RHCE 一、文本搜索工具--grep1、作用2、格式3、参数4、注意5、示例5.1 操作对象文件&#xff1a;/etc/passwd5.2 grep过滤命令示例 二、正则表达式1、概念2、基本正则表达式2.1 常见元字符2.2 POSIX字符类2.3 示例 3、扩展正则表达式3.1 概念3.2 示例 三、作业1、作业一2、作业…

【Apollo】开启Apollo之旅:让自动驾驶如此简单

前言 Apollo 是百度公司推出的自动驾驶平台。它是一个综合性的自动驾驶解决方案&#xff0c;提供了包括感知、决策、规划和控制等核心功能&#xff0c;以及地图、定位、仿真、数据管理等配套工具。 文章目录 前言Apollo 的发展历程Apollo 8.0新特性软件包管理感知框架工具链小…

软件确认测试的七大准则

确认测试 1. 确认软件设计是否依从于软件需求&#xff0c;且软件的每一项需求是否能跟踪到软件设计。 2. 确认状态顺序和状态变化( 功能模块图) 。 3. 确认数据和控制流满足安全性、功能性和性能需求。 4. 确认软件的功能性&#xff0c;硬件、软件和用户接口的一致性&#…

上海亚商投顾:沪指放量大涨超1% 北向资金净买入近70亿元

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 三大指数昨日集体反弹&#xff0c;沪指高开高走涨超1%&#xff0c;上证50盘中涨超2%&#xff0c;北证50指数涨…

涉案财物管理系统DW-S405|实现涉案财物智能化管理

涉案财物管理系统&#xff08;智财物DW-S405&#xff09;是依托互3D技术、云计算、大数据、RFID技术、数据库技术、AI、视频分析技术对RFID智能仓库进行统一管理、分析的信息化、智能化、规范化的系统。 东识涉案财物管理系统是一套基于物联网技术的管理方案&#xff0c;旨在规…

孙哥Spring源码第17集

第17集 refresh()-invokeBeanFactoryPostProcessor -一-invokeBeanFactoryPostProcessor的分析过程 【视频来源于&#xff1a;B站up主孙帅suns Spring源码视频】 1、什么是解析顶级注解&#xff1f; PropertySource CompeontScan Configuration Component ImportResour…

第14章 热门数据实时收集

mini商城第14章 热门数据实时收集 一、课题 商品秒杀-热门数据实时收集 二、回顾 1、秒杀设计 2、活动管理 3、搜索管理 4、商品详情处理 5、 数据同步 三、目标 1、掌握热门分析收集方案 2、Lua高级语法 掌握Lua高级语法指令执行顺序 掌握Lua高级语法指令编写位置 …

FPGA GTH 全网最细讲解,aurora 8b/10b编解码,HDMI视频传输,提供2套工程源码和技术支持

目录 1、前言免责声明 2、我这里已有的 GT 高速接口解决方案3、GTH 全网最细解读GTH 基本结构GTH 发送和接收处理流程GTH 的参考时钟GTH 发送接口GTH 接收接口GTH IP核调用和使用 4、设计思路框架视频源选择silicon9011解码芯片配置及采集动态彩条视频数据组包GTH aurora 8b/10…

el-select 加多选框使用

解决方法&#xff1a; el-select 添加属性 multiple&#xff0c; <el-form-item label"订单来源&#xff1a;"><el-selectv-model"tableFrom.userType"clearablemultipleplaceholder"请选择"class"selWidth"><el-opt…

Android图形-架构1

目录 引言 Android图形的关键组件&#xff1a; Android图形的pipeline数据流 BufferQueue是啥&#xff1f; 引言 Android提供用于2D和3D图形渲染的API&#xff0c;可与制造商的驱动程序实现代码交互&#xff0c;下面梳理一下Android图形的运作原理。 应用开发者通过三种方…

pm2 运行springboot项目

创建springboot项目打包 注意安装java版本必须和打包的java版本一致 java安装 安装pm2 安装nodejs 通过npm安装pm2 配置json pm2.json {"name": "test01","script": "D:\\jdk-17.0.6\\bin\\java.exe","args": ["-D…

无涯教程-JavaScript - EDATE函数

描述 EDATE函数返回表示日期的序列号,该序列号是在指定日期(start_date)之前或之后的月份数。 使用EDATE来计算到期日或到期日,该到期日或到期日与发行日期在当月的同一天。 语法 EDATE (start_date, months)争论 Argument描述Required/OptionalStart_date 代表开始日期的…

2023年特色小镇行业研究报告

第一章 行业概况 1.1 定义 特色小镇&#xff0c;是指以特定产业、文化、旅游、历史等特色为依托&#xff0c;以小镇为载体&#xff0c;通过优化提升小镇的综合功能和服务能力&#xff0c;形成独特的区域品牌和产业集群&#xff0c;进而推动区域经济社会发展的一种新型城镇化形…

【Unity编辑器扩展】| 顶部菜单栏扩展 MenuItem

前言【Unity编辑器扩展】 | 顶部菜单栏扩展 MenuItem一、创建多级菜单二、创建可使用快捷键的菜单项三、调节菜单显示顺序和可选择性四、创建可被勾选的菜单项五、右键菜单扩展5.1 Hierarchy 右键菜单5.2 Project 右键菜单5.3 Inspector 组件右键菜单六、AddComponentMenu 特性…

AUTOSAR简介及分层架构

这里写目录标题 1、AUTOSAR简介2、AUTOSAR的分层架构2.1、应用软件层(ASW)2.2、运行时环境(RTE)2.3、基础软件层(BSW)2.4、微控制器-MCAL层 1、AUTOSAR简介 AUTOSAR&#xff08;AUTomotive Open System ARchitecture&#xff09;是一种为汽车软件开发提供开放化、标准化、可重…

一篇文章教会你SpringMVC

目录 1.什么是SpringMVC 2.SpringMVC工作流程 3.SpringMVC核心组件 4.SpringMVC的配置流程 4.1导入POM依赖 4.2在WEB-INF下添加springmvc-servlet.xml(spring-mvc.xml) 4.3 修改web.xml 创建一个Controller用来存放web层的方法和内容 创建一个前端页面用来做测试展示 前言…