C++11的可变参数模板

可变参数模板

  • 什么是可变参数
  • 模板的可变参数
  • 展开参数包
  • emplace系列函数
    • 引例
    • emplace系列函数

什么是可变参数

printf和scanf中就涉及可变参数

在这里插入图片描述

这里三个点就代表可变参数,意思就是不管你传多少个参数,都可以接收

printf("%d",x);
printf("%d%d",x,y);
printf("%d%d%d",x,y,z);

模板的可变参数

C++98中类模版和函数模版中只能含固定数量的模版参数。C++11中新增可变参数模板能够让您创建可以接受可变参数的函数模板和类模,相比

样例:

template <class ...Args>
void ShowList(Args... args)
{}
  • 形参args前面有…,所以args就是一个可变模板参数,我们把带省略号的参数称为“参数包”,它里面包含了0到N(N>=0) 个模版参数
  • Args是一个模板参数包,args是一个函数形参参数包(这里的Args和args就是个名称,一般都叫这个)

使用示例:

在这里插入图片描述

展开参数包

我们无法直接通过args[i]这样方式获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特点,也是最大的难点,即如何展开可变模版参数。

下面介绍两种展开参数包的方式:

  • 第一种:递归函数方式展开参数包
//递归终止函数:当参数包中没有参数时,走此函数,结束递归
void _showlist()
{cout << endl;
}//当参数包中参数个数不为0时,走此函数,进行递归
template<class T, class ...Args>
void _showlist(T value , Args... args)
{cout << value << " ";_showlist(args...);
}//该函数能够接收任意参数个数的参数包
template< class ...Args>
void showlist(Args... args)
{_showlist(args...);
}int main()
{showlist();showlist(1);showlist(1,2);showlist(1,2,2.2);showlist(1,2,2.2,"xxx"); return 0;
}

首先,showlist函数可以接收任意实参个数,进入参数包args中;然后根据参数包args中参数的个数调用_showlist

当参数包中参数个数大于0时,就调用void _showlist(T value , Args... args),每调用一次这个函数,参数包中的参数个数就减1(原参数包中第一个参数给到value,剩下的参数进入新的参数包),直至参数包中参数个数为0时,调用void _showlist(),递归结束

这种方式就是利用递归函数不断将形参参数包一个一个的拆解下来

  • 第二种:逗号表达式展开参数包(看看就好,抽象得很)
template <class T>
void PrintArg(T t)
{cout << t << " ";
}//参数包个数为0时
void ShowList()
{cout << endl;
}//展开函数
template <class ...Args>
void ShowList(Args... args)
{int arr[] = { (PrintArg(args), 0)... };cout << endl;
}int main()
{ShowList();ShowList(1);ShowList(1, 'A');ShowList(1, 'A', std::string("sort"));return 0;
}

这种展开参数包的方式,不需要通过递归终止函数,是直接在expand函数体中展开的, printarg不是一个递归终止函数,只是一个处理参数包中每一个参数的函数。

这种就地展开参数包的方式实现的关键是逗号表达式。我们知道逗号表达式会按顺序执行逗号前面的表达式。expand函数中的逗号表达式:(printarg(args), 0),也是按照这个执行顺序,先执行printarg(args),再得到逗号表达式的结果0。同时还用到了C++11的另外一个特性——列表初始化,通过列表初始化来初始化一个变长数组, {(printarg(args), 0)...}将会展开成{(printarg(arg1),0),(printarg(arg2),0), (printarg(arg3),0), etc... },最终会创建一个元素值都为0的数组int arr[sizeof...(Args)]

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

emplace系列函数

可变参数模板最常见的使用场景就是:emplace系列的函数

引例

我们先看一个可变参数模板的使用引例

class Date
{
public:Date(int year = 1, int month = 1, int day = 1):_year(year),_month(month),_day(day){}
private:int _year;int _month;int _day;
};template<class ...Args>
Date* Create(Args... args)
{Date* ret = new Date(args...);return ret;
}int main()
{Date* p1 = Create();Date* p2 = Create(2023);Date* p3 = Create(2023,9);Date* p4 = Create(2023,9,27);Date d(2023, 2, 2);Date* p5 = Create(d);return 0;
}

上述代码中:

  • p1-p4分别向参数包中传了0至3个参数,而这四种情况都可以成功匹配到Date的全缺省构造函数

  • p5传了一个Date类对象进入参数包,而这种情况会匹配Date类中的默认拷贝构造函数

这就是可变参数模板的优势,使得传参非常的灵活,会自动根据参数包中的参数去匹配最适合的函数

emplace系列函数

  1. 场景一:

我们以list为例:std::list< std::pair<int, char> > mylist;

定义了一个list,节点的中的值类型为pair

当我们想尾插一个节点时:

  • 以前会这样写:mylist.push_back(make_pair(40,'d'));或者用c++11的列表初始化mylist.push_back({40,'d'});,这两者本质上是一样的。

  • 有了emplace系列函数后,可以这样写:mylist.emplace_back(40,'d');

    在这里插入图片描述

不管是push_back还是emplace_back其实参类型都应该是pair,所以上述两种写法的不同之处在于pair对象的产生。如同开头的Date引例一样,第一种产生pair对象是拷贝构造,而第二种产生pair对象是直接构造

在这种场景下,emplace_backpush_back效率上其实差不了多少

  1. 场景二:

还是以list为例:std::list< std::pair<int, xy::string> > mylist;

这里pair的第二个参数类型是string(涉及深拷贝的自定义类型)

当我们想尾插一个节点时:

  • 以前:mylist.push_back(make_pair(30, "sort"));
  • 用emplace:mylist.emplace_back(10, "sort");

在这里插入图片描述

对比发现:第一种由于是拷贝构造生成的pair类型,因此在拷贝时会调用移动拷贝去拷贝string;第二种调用的是pair的构造函数,因而省去了移动拷贝

但这里也并不能体现出,emplace系列的优越,因为前者也就比后者多了个移动构造,但移动构造的代价其实很低

  1. 场景三:

真要说emplace_back比push_back更高效,在内置类型上体现更明显

还是以list为例:std::list<Date> mylist

当进行尾插时:

  • 第一种: mylist.push_back(Date(2023,9,27));
  • 第二种:mylist.emplace_back(2023,9,27);

第一种:是构造+拷贝构造,因为push_back的类型必须是Date。所以这里是先将Date构造出来,然后往下传,传到list_node那里new Node时就是拷贝构造

第二种:可以把参数包一直往下传,传到list_node那里new Node时直接依据参数包匹配Date类中的构造函数来初始化Date类型的对象

此外,第一种只能传日期类对象;第二种既可以传日期类对象,还可以传参数包

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

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

相关文章

狼牙山短视频:成都柏煜文化传媒有限公司

狼牙山短视频&#xff1a;记录自然与历史的交融 随着短视频的兴起&#xff0c;我们得以在短短几分钟内&#xff0c;跨越千山万水&#xff0c;领略世界各地的风情。成都柏煜文化传媒有限公司 而今天&#xff0c;我想带大家走进一个独特的地方——狼牙山&#xff0c;通过一系列短…

Transformer教程之Transformer的历史背景

在现代人工智能领域&#xff0c;Transformer模型已经成为一种不可或缺的技术&#xff0c;它在自然语言处理&#xff08;NLP&#xff09;和计算机视觉等多个领域取得了巨大的成功。本文将带你回顾Transformer的历史背景&#xff0c;了解它是如何从最初的构想到今天的广泛应用的。…

Web渗透:文件包含漏洞

Ⅱ.远程文件包含 远程文件包含漏洞&#xff08;Remote File Inclusion, RFI&#xff09;是一种Web应用程序漏洞&#xff0c;允许攻击者通过URL从远程服务器包含并执行文件&#xff1b;RFI漏洞通常出现在动态包含文件的功能中&#xff0c;且用户输入未经适当验证和过滤。接着我…

生产者发送数据,kafka服务器接收数据异常的问题记录

现象&#xff1a; 某个客户要求审计日志用kafka的方式传输给他们&#xff0c;使用了第三方的librdkafka库来开发。 往客户提供的kafka服务器上的一个topic发送数据&#xff0c;这个topic有三个分区&#xff0c;客户反馈接收到的数据和发送端发送的实际数量对不上&#xff0c;他…

使用VMware创建Ubuntu 24.04【一】

相关链接下载地址 VMware https://www.vmware.com/content/vmware/vmware-published-sites/cn/products/workstation-pro/workstation-pro-evaluation.html.html.html Ubuntu 24.04 LTS https://cn.ubuntu.com/download/desktop 虚拟机创建 1、打开VNware软件&#xff0c;点…

5.9k!一款清新好用的后台管理系统!【送源码】

今天给大家分享的开源项目是一个优雅清新后台管理系统——Soybean Admin。 简介 官方是这样介绍这个项目的&#xff1a; Soybean Admin 使用的是Vue3作为前端框架&#xff0c;TypeScript作为开发语言&#xff0c;同时还整合了NaiveUI组件库&#xff0c;使得系统具有高可用性和…

基于YOLOv5+pyqt5的口罩佩戴检测系统(PyQT页面+YOLOv5模型+数据集)

简介 在各种工作环境和公共场所,确保人们正确佩戴口罩对个人防护和公共卫生至关重要,尤其是在医疗设施、制造业车间和拥挤的公共交通中。为了满足这一需求,我们开发了一种基于YOLOv5目标检测模型的口罩佩戴检测系统。本项目不仅实现了高精度的口罩佩戴检测,还设计了一个可…

学习提示词工程

去年 11 月 8 日&#xff0c;新加坡政府科技局&#xff08;GovTech&#xff09;组织举办了首届 GPT-4 提示工程&#xff08;Prompt Engineering&#xff09;竞赛。数据科学家 Sheila Teo 最终夺冠&#xff0c;成为最终的提示女王&#xff08;Prompt Queen&#xff09;。之后&am…

Swagger2及常用校验注释说明

Api(value "后台用户管理") RestController RequestMapping("bossuser") public class BossUserController {ApiOperation(value "测试接口")PostMapping("test")public String testUser(Valid RequestBody TestUser user) {LOG.inf…

机器学习之集成学习

一&#xff1a;概念 顾名思义集成学习就是用多个其他的算法结合起来使用 对于“其他算法”有同类和同质的区别&#xff0c;同质指的是所用的算法都是同一类型的&#xff0c;比如决策树和神经网络&#xff0c;这种也叫基学习器。反之亦然&#xff0c;但一般使用的是同质的。 …

6种高效便捷的移动硬盘加密软件,总有一款适合你

想要给自己移动硬盘内的文件/文件夹加密来保护数据隐私&#xff0c;防止重要信息泄露&#xff1f;使用电脑文件夹加密工具可以轻松帮您解决&#xff01;面对市面上众多的加密工具&#xff0c;如何选择成为一大难题。本文将为您提供一份详细的挑选指南&#xff0c;帮助您选择最合…

Java程序员接单的十条“野路子”,分分钟收入20K!

Java程序员除了主业工作外&#xff0c;也要适当扩展兼职接单这条路。毕竟Java接单可以说是Java程序员进行技术变现的最佳方式之一。 因为Java程序员兼职接单的难度相对更低&#xff0c;单量也比较可观&#xff0c;最重要的是性价比也很顶&#xff0c;且听我一一道来&#xff1a…

2024年6月24日 (周一) 叶子游戏新闻

图吧工具箱: 全名图拉丁吧硬件检测工具箱,是开源、免费、绿色、纯净的硬件检测工具合集,专为图钉及所有DIY爱好者制作,包含常用硬件测试和检测工具,月工JS必备! 土豆录屏: 免费、无录制时长限制、无水印的录屏软件 高手在民间 粉丝玩家打造精美《黄金树幽影》巨大插画虽然不是专…

大数据------额外软件、插件及技术------Linux(完整知识点汇总)

Linxu 不同领域的主流操作系统 桌面操作系统 WindowsMAac OSLinux 服务器端操作系统 UNIX&#xff08;付费&#xff09;LinuxWindows Server&#xff08;付费&#xff09; 移动设备操作系统 Android&#xff08;基于Linux开源&#xff09;IOS&#xff08;不开源&#xff09; 嵌…

Three.js鼠标拖动设置骨骼姿态

实现 根据SkinnedMesh生成Mesh 作为射线检测的目标&#xff08;射线检测SkinnedMesh存在不足 无法应用骨骼形变的顶点 &#xff09;点击模型 获取点击位置对应的骨骼拖拽鼠标设置骨骼旋转角度&#xff08;使用TransformControl选中点击的骨骼 设置轴为XYZE 并隐藏控件 主动触发…

PostgreSQL计算 queryid 原理

数据库版本 PG 16.1 queryid 是什么 queryid 是将 sql 规范化 (normalization) 后&#xff0c;通过哈希函数计算出来的 64 位整数。 以 SELECT id, data FROM tbl_a WHERE id < 300 ORDER BY data; 这条 SQL 为例。当我们在 PG 中执行这条 sql 时&#xff0c;内核在语义…

【STM32-DAP 仿真器】

STM32-DAP 仿真器 ■ STM32-DAP仿真器介绍■ STM32-DAP仿真特点■ STM32-DAP仿真器实物图■ STM32-DAP高速 DAP 仿真器实物图■ STM32-DAP高速无线调试器 实物图■ STM32-DAP高速无线调试器示意图■ STM32-DAP高速无线调试器接线图■ STM32-DAP高速无线调试器接收端示意图 ■ S…

vcruntime140_1.dll是什么东东?vcruntime140_1.dll缺失的8个解决方法

当电脑出现找不到vcruntime140_1.dll,或vcruntime140_1.dll丢失无法打开软件怎么办&#xff1f;小编今天在本文详细为大家介绍解决方法与介绍vcruntime140_1.dll究竟是什么等vcruntime140_1.dll的问题。 一、vcruntime140_1.dll文件是什么 文件概述定义与功能 vcruntime140_…

CAN收发器

1、收发器的主要功能 &#xff08;1&#xff09;CAN通讯&#xff08;即报文收发&#xff09; MCU要CAN通讯&#xff1a;收发器模式切换至正常通讯模式&#xff08;Normal&#xff09;&#xff0c;正常通讯模式收发器能收能发。 MCU不要CAN通讯&#xff1a;把收发器切换至其它…

format()函数

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 语法介绍 format()可以对数据进行格式化处理操作&#xff0c;语法如下&#xff1a; format(value, format_spec) format_spec为格式化解释。当参数…