【C++11】可变参数模板

【C++11】可变参数模板

一、可变参数模板概念以及定义方式

​ 在C++11之前,类模板和函数模板只能含有固定数量的模板参数,c++11增加了可变模板参数特性:允许模板定义中包含0到任意个模板参数。声明可变参数模板时,需要在typename或class后面加上省略号“…”。

//Args是一个模板参数包,args是一个函数形参参数包
//声明一个参数包Args... args,这个参数包中可以包含0到任意个模板参数。
//模板参数包Args和函数参数包args的名字可以任意指定,并不是说必须叫做Args和args
template<class... Args>
void ShowList(Args... args)
{}

​ 现在调用ShowList函数时就可以传入任意多个参数了,并且这些参数可以是不同类型的。比如:

template<class ...Args>
void ShowList(Args... args)
{}
int main() {ShowList(1);ShowList(1, 'A');ShowList(1, 'A', string("sort"));return 0;
}

​ 我们可以在函数模板中通过sizeof计算参数包中参数的个数。比如:

template<class ...Args>
void ShowList(Args... args){cout << sizeof...(args) << endl;//获取参数包中参数的个数
}

image-20240916144446848

二、参数包的展开

​ 上面的参数args前面有省略号,所以它就是一个可变模板参数,我们把带省略号的参数称为“参数包,它里面了0到N(N>=0)个模板参数。我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方法来获取参数包中的每个参数,这是使用可变模板参数的一个主要特点,也是最大的难点,即如何展开可变模板参数。由于语法不支持使用args[i]这样方式获取可变参数,所以我们得用一些奇招来一一获取参数包的值。

1.递归函数方式展开参数包

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

  • 给函数模板增加一个模板参数,这样就可以从接收到的参数包中分离除一个参数出来。
  • 在函数模板中递归调用该函数模板,调用时传入剩下的参数包。
  • 如此递归下去,每次分离出参数包中的一个参数,直到参数包中的所有参数都被取出来
  • 还需要给递归终止函数。
//递归终止函数
//当递归调用ShowList函数模板时,如果传入的参数包中参数的个数为0,那么就会匹配到这个无参的递归终止函数,这样就结束了递归
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', string("sort"));return 0;
}

image-20240916145528517

2.逗号表达式展开参数包

​ 我们知道逗号表达式会按顺序执行逗号前面的表达式。
​ (PrintArg(args),0),也是按照这个执行顺序,先执行PrintArg(args),再得到逗号表达式的结果0。同时还用到了C++11的另外一个特性 —— 初始化列表,通过初始化列表来初始化一个变长数组,{(PrintArg(args),0) … }将会展开成((PrintArg(arg1),0),(PrintArg(arg2),0),(PrintArg(args),0),etc…),最终会创建一个元素值都为0的数组 int arr[sizeof…(Args)]。
​ 由于是逗号表达式,在创建数组的过程中会先执行逗号表达式前面的部分printarg(args) 打印出参数,也就是说在构造int数组的过程中就将参数包展开了,这个数组的目的纯粹是为了在数组构造的过程展开参数包。

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', string("sort"));return 0;
}

image-20240916150956840

三、STL容器中的emplace相关接口函数

​ C++11标准给STL中的容器增加emplace版本的插入接口,比如vector容器的push_back和insert函数,都增加了对应的emplace_back和emplace函数。如下:
image-20240916151809639

​ 我们来看一下他们的声明

image-20240916151836714

​ push_back在C++之后除了原来的左值版本外,还提供了右值版本,如果push_back的是左值那么就调用左值版本,反之就是右值引用的版本;但是只能支持单个元素的插入。
​ emplace_back和emplace 与以前的接口的本质的区别就是他们采用了可变参数模板,这也一来,他就可以支持多个元素的插入,代码如下:

int main() {/********************emplace_back***************************/vector<int> v1;vector<int> v2;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);//v1.push_back({1,2,3,4,5}); // 不可以v2.emplace_back(1, 2, 3, 4, 5);/**********************emplace*************************/v1.insert(v1.begin(), 9);v1.insert(v1.begin(), 8);v1.insert(v1.begin(), 7);v1.insert(v1.begin(), 6);//v1.insert(v1.begin(), 9, 8, 7, 6); // 不可以v2.emplace(v2.begin(), 9, 8, 7, 6);return 0;
}

​ 由于emplace系列接口的可变参数的类型都是万能引用,因此既可以接收右值对象,还可以接收参数包。

  • 如果调用emplace系列接口时传入的是左值对象,那么首先需要先在此之前调用构造函数实例化出一个左值对象,最终在使用定位new表达式调用构造函数对空间进行初始化,会匹配到拷贝构造函数。
  • 如果调用emplace系列接口时传入的是右值对象,那么就需要在此之前调用构造函数实例化出一个右值对象,最终在使用定位new表达式调用构造函数对空间进行初始化时,就会匹配到移动拷贝函数。
  • 如果调用emplace系列接口时传入的是参数包,那就可以直接调用函数进行插入,并且最终在使用定位new表达式调用构造函数对空间进行初始化时,匹配到的是构造函数。

总结一下:

  • 传入左值对象,需要调用构造函数+拷贝构造函数
  • 传入右值对象,需要调用拷贝构造+移动构造函数
  • 传入参数包,只需要调用构造函数

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

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

相关文章

【课程学习】信号检测与估计II

b站 文章目录 1-概述 1-概述 线性、正交、平稳、高斯 研究线性模型&#xff0c;采用正交化方法&#xff0c;假设信号平稳&#xff0c;考虑信号的统计特性是高斯的。 本学期考虑&#xff0c;非线性、非正交、非平稳、非高斯。 阵列处理 1980-1990 MUSIC 稀疏性 2006-2012 LASS 时…

[SaaS] FacyTech

Sora还没开源,但这家国产AIGC视频公司已经靠还原现实赚钱了我们找到了朱啸虎说“很酷”的公司https://mp.weixin.qq.com/s/rm_dylLhf4FP01c_hdU3Lw1.tryon 这图ootdiffusion+comfyui工作流吗?lora+controlnet openpose+ipa

最新R(4.4.1)及R-studio保姆级安装配置详细教程及常见问题解答

我不吃我不喝我就要安装好R和rstudio 大家是不是还在为如何安装好rstudio而烦恼呢!&#xff1f;没事的没事的&#xff0c;今天小编就手把手教会大家安装&#xff0c;保管看完就会&#xff0c;安装完成&#xff01;&#xff01;&#xff01; 安装前先关闭360杀毒软件以及防火墙…

windows系统安装docker

参考&#xff1a;GitHub - tech-shrimp/docker_installer: Docker官方安装包&#xff0c;用来解决因国内网络无法安装使用Docker的问题 1.windows系统安装docker cmd 右键 以管理员身份运行 输入 wsl --set-default-version 2 wsl --update --web-download GitHub - tech-s…

一起对话式学习-机器学习02——机器学习方法三要素

【一】核方法 首先补充一下核方法&#xff0c;这应是机器学习分类中的内容。 什么是核方法呢&#xff1f;听起来很高级&#xff0c;但理解很简单&#xff1a; 官方定义&#xff1a;核方法是使用核函数表示和学习非线性模型的一种机器学习方法&#xff0c;可以用于监督学习和非监…

如何解决“json schema validation error ”错误? -- HarmonyOS自学6

一. 问题描述 DevEco Studio工程关闭后&#xff0c;再重新打开时&#xff0c;出现了如下错误提示&#xff1a; json schema validation error 原因&#xff1a; index.visual或其他visual文件中的left等字段的值为负数时&#xff0c;不能以”-0.x“开头&#xff0c;否则就会…

堆+堆排序+topK问题

目录 堆&#xff1a; 1、堆的概念 2、堆的结构 3、堆的实现 3.1、建堆 3.1.1、向上调整建堆(用于堆的插入) 3.1.2、向下调整建堆 3.2、堆的删除 3.3、堆的代码实现 3.3.1、Heap.h 3.3.2、Heap.c 堆排序&#xff1a;&#xff08;O(N*log(N))&#xff09; 1、排序如何…

yjs06——numpy的介绍与优势(1)

1.numpy是什么&#xff1f; numpy是python的一个科学计算库&#xff0c;用于快速处理 任意维度的数据&#xff1b; numpy的存储单元/基本数据类型是 ndarray&#xff08;多维数组&#xff09; 2.多维数组的建立&#xff1a; import numpy as np np.array([ [1,2,3], [4,5,6…

ZYNQ7010_7020_硬件LVDS设计

ZYNQ7010_7020_硬件LVDS设计 ZYNQ7010_7020_硬件LVDS设计 1.版本说明2.概述3.目标4.硬件设计5.IO SERDES 1.版本说明 日期作者版本说明20240916风释雪初始版本 2.概述 当我们使用ZYNQ7010/15/20的时候&#xff0c;本身BANK只支持HR&#xff0c;不支持HP, 如图&#xff1a; …

Flink有界流实现(1)

flink实现有界流需要使用StreamExecutionEnvironment类&#xff0c;并且最后需要使用env.execute() 方法&#xff0c;有界和无界的算子有时候会有不同的 复杂的写法 package org.example.test; import org.apache.flink.api.java.functions.KeySelector; import org.apache.fl…

Redis的缓存穿透、缓存雪崩、缓存击穿怎么解决

Redis在实际使用中是会遇到很多问题的&#xff0c;例如今天说到的缓存穿透、缓存雪崩、缓存击穿。 缓存穿透&#xff1a; 缓存穿透是指客户端请求的数据在redis缓存和数据中都不存在&#xff0c;这样缓存永远都不会生效&#xff0c;这些请求都会打到数据库当中&#xff0c;对…

【CTF MISC】XCTF GFSJ1088 [中等] QR1 Writeup(图像处理+QR Code识别)

[中等] QR1 一张空白的图片&#xff1f; 解法 一张空白图片。 用 Photoshop 打开&#xff0c;放大&#xff0c;发现很多小黑点。 将图片复制到新文档&#xff0c;用魔棒工具选择白色部分。 Ctrl Shift i 反选。编辑&#xff0c;描边&#xff0c;黑色&#xff0c;10px&#…

java面向对象:构造方法

给出javabean类代码 package google.test5;public class Student {private String name;private int age;public Student(){System.out.println("看看我打印了嘛&#xff1f;");}public Student(String name, int age){this.name name;this.age age;}public void …

2024 年高教社杯全国大学生数学建模竞赛 B 题 生产过程中的决策问题 第二问chatGPT4的回答,matlab和python代码

持续更新中,2024年所有 数学建模比赛思路代码都会发布到专栏内,只需要订阅一次。 5号6号半价,会结合历年优秀论文、人工智能深度学习算法、chatgpt。会定期发布思路、代码和论文。思路和论文基本拿不到国奖,想要获得国奖的同学不要购买。适合基础差的学生,只适合冲击省奖。…

PMP--一模--解题--61-70

文章目录 14.敏捷61、 [单选] 作为估算活动持续时间过程的一部分&#xff0c;项目经理促成了与产品负责人和Scrum团队的冲刺计划会议。项目经理将用户故事分解为较小的任务项&#xff0c;以小时为单位估算所需时间&#xff0c;并根据团队的能力确定冲刺待办事项列表。尽管计划周…

您使用过哪些AI集成工具提升工作效率

您使用过哪些AI集成工具提升工作效率 随着AI技术的飞速发展&#xff0c;个人开始寻求高效的方法来构建和管理定制化模型&#xff0c;以简化复杂的开发过程&#xff0c;提高工作效率。说起用AI集成工具来提高工作效率&#xff0c;个人作为开发者&#xff0c;确实在使用AI代码辅助…

引用和指针的区别(面试概念性题型)

个人主页&#xff1a;Jason_from_China-CSDN博客 所属栏目&#xff1a;C系统性学习_Jason_from_China的博客-CSDN博客 所属栏目&#xff1a;C知识点的补充_Jason_from_China的博客-CSDN博客 概念概述 内存占用&#xff1a; 引用&#xff1a;引用一个变量时&#xff0c;实际上并…

.ipynb 图解介绍,轻松入门,提升效率

目录 01 使用jupyter遇到的问题1.1 Python requires ipykernel installed or requires an update 1.1.1 查询所有内核 1.1.2 选择对应的Python版本 02 理解jupyter规则 2.1 系统命令 01 使用jupyter遇到的问题 1.1 Python requires ipykernel installed or requires an up…

IP 协议分析《实验报告》

目录 一、 实验目的 二、实验设备和环境 三、实验记录 1、实验环境搭建 2、IP 协议分析 1.设置抓包接口 2.IP 报文分析 3.报文长度计算 4.生存时间 TTL 5.分析总结 3、IP分片 1.IP 分片简介 2.捕获分组 3.结果分析 一、 实验目的 1、掌握 IP 协议数据报格式&…

CPLEX+Yalmip+MATLAB2022a配置

来源&#xff1a;yalmipcplex12.10文件及安装教程-CSDN博客https://blog.csdn.net/qq_41944352/article/details/126421198 安装包 来源&#xff1a;yalmipcplex12.10文件及安装教程-CSDN博客 Cplex 需下载&#xff1a; Microsoft Visual C 2015 Redistributable 添加路径&a…