c++可变参数模板

news2024/11/26 20:26:23
不要做一个清醒的堕落者

文章目录

  • 可变参数模板的简介
    • 什么是可变参数
  • 模板参数包
    • 参数包数据的获取(函数递归获取)
    • 参数包的获取(逗号表达式获取)
  • 可变参数的应用emplace

可变参数模板的简介

c++11添加的新特性能够让你创建可以接受改变的函数模板和类模板,C++98/03,类模版和函数模版中只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改进。

什么是可变参数

首先我们先来介绍一下什么是可变参数,我们先从函数说起吧,我们想想可变是什么意思,我们在写一个函数的时候我们的函数形参一般数量都是固定的,比如Add函数,max函数,min函数都是只能传递两个参数,那么有哪些函数可以传递的形参数目是不固定的呢?那当然是printf scanf,这两个函数的参数,数量都是变化的,并且参数类型也是,那么这是如何做到的呢?其实就是使用了可变参数模板,那么printf和scanf的底层其实使用了一个类似于数组的一个东西,而惊天我们讲述的则是更为高级些的类的可变参数。

模板参数包

首先我们先说一下格式

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

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

参数包数据的获取(函数递归获取)

那么我们可以通过什么方式呢?这里其实比较好的办法是通过函数递归进行展开。
代码如下

#include<iostream>
using namespace std;
template<class T>
void func(T val)
{
	cout << val << " ";
}
template<class T,class ...Args>
void func(T val, Args... args)
{
	cout << val<<" ";
	func(args...);
}
int main()
{
	func(1, 2, 3, 4, 5);

	return 0;
}

这里的递归展开图呢我给大家画一下
在这里插入图片描述
那么这里呢就是这个函数的递归展开图其实这个args大家如果不理解可以将它看成是一个背包,这个背包可以是空的,然后可以把数据放进去,然后利用函数重载,将其一个一个取出来。其次呢我们就是只有一个参数的拿个函数我们称之为终止函数,因为有了这个函数才使得当这个背包里只剩下一个数据的时候才能有函数可调用,表示终止了。当然了也不止这一个方法。

参数包的获取(逗号表达式获取)

这种展开参数包的方式,不需要通过递归终止函数,是直接在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数组的过程中就将参数包展开了,这个数组的目的纯粹是为了在数组构造的过程展开参数包

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;
}

这里给大家说明一下,这里其实数组后面的三个点是交给编译器去推的,也就是说编译器眼里这里是怎们处理了呢?

int arr[]={(PrintArg(args), 0),(PrintArg(args), 0),(PrintArg(args), 0)}//是这个样子的

那么让我们自己去写肯定是不行的太麻烦所以交给编译器去推导,当然了我认为还可以更加简化一些简化为下面这个代码更好

#include<iostream>
#include<string>
using namespace std;
template <class T>
int  PrintArg(T t)
{
	cout << t << " ";
	return 0;
}
//展开函数
template <class ...Args>
void ShowList(Args... args)
{
	int arr[] = { PrintArg(args)... };
	cout << endl;
}
int main()
{
	ShowList(1);
	ShowList(1, 'A');
	ShowList(1, 'A', std::string("sort"));
	return 0;
}

首先我们要理解为什么原来的代码要加逗号表达式里面写个0其实是因为数组不能为空,所以写的,那么我们直接将函数的返回值改为返回0不是也可以解决这个问题吗。

可变参数的应用emplace

emplace的函数声明如下:

template <class... Args>
void emplace_back (Args&&... args);

首先我们看到的emplace系列的接口,支持模板的可变参数,并且万能引用。那么相对insert和
emplace系列接口的优势到底在哪里呢?
在这里插入图片描述
我们看一下图中的这个代码,我们发现emplace_back这样子插入没有报错而push_back这样却报错了,这是为什么呢?其实就是因为emplace_back的底层用了可变参数包。我们可以弄一个简易的类来给大家查看一下。

#include<list>
#include<string>
#include<iostream>
using namespace std;
namespace clzyf {
	class data
	{
	private:
		int year, month, day;
	public:
		data(int _year = 1, int _month = 1, int _day = 1)
			:year(_year)
			, month(_month)
			, day(_day)
		{
			;
		}
		template<class... Arge>
		void crate(Arge... arge)
		{
			data(arge);
		}

	};
}
int main()
{
	clzyf::data a = { 2023,10,14 };
	clzyf::data b={2023,10};
	clzyf::data c={2023};
	clzyf::data d={};
	return 0;
}

请看这里我们通过传值将参数弄成一个参数包并且设置一下缺省参数这样子传递的话就可以弄出更多的花样,那么我们言归正传在上面的那个例子中其实也是这样的,他首先先将传递给emplace的参数弄成一个参数包然后将参数包打包给插入函数,因此就可以做到push_back无法做到的事情了。

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

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

相关文章

深入篇【C++】总结<lambda表达式>与<包装器和bind>的使用与意义

深入篇【C】总结&#xff1c;lambda表达式&#xff1e;与&#xff1c;包装器和bind&#xff1e;的使用与意义 一.lambda表达式1.使用语法2.底层本质3.应用意义 二.包装器(适配器)1.使用语法2.解决问题①3.解决问题②4.应用场景:指令操作 三.bind (适配器)1.调整参数位置2.绑定参…

Kafka基础入门

Kafka介绍 Kafka是什么&#xff1f; kafka是一种分布式的&#xff0c;基于发布/订阅的消息系统。 Kafka的特点 分布式&#xff0c;吞吐量高&#xff0c;发布订阅模式&#xff0c;轻量灵活&#xff0c;较长时间持久化 Kafka的应用场景 解耦 原先一个微服务是通过接口&…

2023年中国芝麻酱行业供需分析:需求量同比增长3.5%[图]

芝麻酱也叫麻酱&#xff0c;是把炒熟的芝麻磨碎制成的食品&#xff0c;有香味&#xff0c;作为调料食用。根据所采用的芝麻的颜色&#xff0c;可分为白芝麻酱和黑芝麻酱&#xff1b;芝麻酱是群众非常喜爱的香味调味品之一。食用以白芝麻酱为佳&#xff0c;滋补益气的以黑芝麻酱…

动态内存管理(malloc calloc realloc free)--- C语言

文章目录 写在前面1. malloc 和 free函数1.1 malloc函数介绍1.2 free函数介绍 2. calloc函数3. realloc函数4. 常见的动态内存错误4.1 对NULL指针的解引用操作4.2 对动态开辟空间的越界访问4.3 对非动态开辟内存使用free释放4.4 使用free释放一块动态开辟内存的一部分4.5 对同一…

Windows 移动设备管理

Windows 设备管理是指一组流程和工具&#xff0c;可帮助 IT 管理员简化企业中使用的Windows 设备的管理。管理企业中使用的 Windows 设备最好通过实施Windows MDM 解决方案来完成&#xff0c;以从单个控制台保护、管理和监视这些设备。Windows移动设备管理 &#xff08;MDM&…

CakePHP 3.x/4.x反序列化RCE链

最近网上公开了cakephp一些反序列化链的细节&#xff0c;但是没有公开poc&#xff0c;并且网上关于cakephp的反序列化链比较少&#xff0c;于是自己跟一下 &#xff0c;构造pop链。 CakePHP简介 CakePHP是一个运用了诸如ActiveRecord、Association Data Mapping、Front Contr…

美团Leaf使用

简介 在复杂分布式系统中&#xff0c;往往需要对大量的数据和消息进行唯一标识。如在美团点评的金融、支付、餐饮、酒店、猫眼电影等产品的系统中&#xff0c;数据日渐增长&#xff0c;对数据分库分表后需要有一个唯一ID来标识一条数据或消息&#xff0c;数据库的自增ID显然不…

全面解析找不到msvcr100.dll的解决方法,快速修复系统msvcr100.dll丢失问题!

在计算机的使用过程中&#xff0c;我们可能会遇到各种问题&#xff0c;其中之一就是“msvcr100.dll丢失”的问题。这个问题通常出现在运行某些程序或游戏时&#xff0c;提示找不到msvcr100.dll文件。这个文件是Microsoft Visual C 2010 Redistributable Package的一部分&#x…

学习 MPP 与 SMP 的区别,终于有人讲明白了

文章目录 01 SMPSMP 的典型特征如下&#xff1a;SMP 的缺点如下。 02 分布式MPP计算架构MPP 架构核心原理如下。 导读&#xff1a;当今数据计算领域主要的应用程序和模型可大致分为在线事务处理&#xff08;On-line Transaction Processing &#xff0c;OLTP&#xff09;、决策…

山西电力市场日前价格预测【2023-10-14】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2023-10-14&#xff09;山西电力市场全天平均日前电价为221.03元/MWh。其中&#xff0c;最高日前电价为341.15元/MWh&#xff0c;预计出现在18: 45。最低日前电价为0.00元/MWh&#xff0c;预计出…

招投标系统软件源码,招投标全流程在线化管理

功能描述 1、门户管理&#xff1a;所有用户可在门户页面查看所有的公告信息及相关的通知信息。主要板块包含&#xff1a;招标公告、非招标公告、系统通知、政策法规。 2、立项管理&#xff1a;企业用户可对需要采购的项目进行立项申请&#xff0c;并提交审批&#xff0c;查看所…

lv8 嵌入式开发-网络编程开发 17 套接字属性设置

1 基本概念 设置套接字的选项对套接字进行控制除了设置选项外&#xff0c;还可以获取选项选项的概念相当于属性&#xff0c;所以套接字选项也可说是套接字属性有些选项&#xff08;属性&#xff09;只可获取&#xff0c;不可设置&#xff1b;有些选项既可设置也可获取 2 选项…

Android组件通信——消息机制(二十六)

1. 消息机制 1.1 知识点 &#xff08;1&#xff09;掌握Message、Handler、Looper类的使用以及消息的传递&#xff1b; &#xff08;2&#xff09;可以通过消息机制动态取得信息&#xff1b; 1.2 具体内容 对于android的消息机制&#xff0c;我们主要要使用Java中线程的一…

医生访问学者出国进修必备面试技巧

医生访问学者出国进修&#xff0c;一直以来都是医学领域内追求更高学术水平和国际化视野的重要途径之一。然而&#xff0c;要成功进入国外院校或研究机构进行进修&#xff0c;首先需要通过面试&#xff0c;因此&#xff0c;面试技巧显得尤为关键。本文知识人网小编将为您介绍一…

LeetCode 739 每日温度(单调栈的初步了解)

1、重新学习了栈的操作&#xff0c;isEmpty()、peek()以及pop()、push()操作 但是值得注意的点是push()必须要有输入 2、单调栈用在这里非常巧妙&#xff0c;通过暴力搜索的方法无法通过最后一个用例 并且通过使用单调栈可以使得时间复杂度从O()降到了O() 3、Deque<Inte…

竞赛 深度学习+opencv+python实现车道线检测 - 自动驾驶

文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络3.1卷积层3.2 池化层3.3 激活函数&#xff1a;3.4 全连接层3.5 使用tensorflow中keras模块实现卷积神经网络 4 YOLOV56 数据集处理7 模型训练8 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &am…

回归预测 | MATLAB实现CNN-LSSVM基于卷积神经网络-最小二乘支持向量机的数据回归预测(多指标,多图)

回归预测 | MATLAB实现CNN-LSSVM基于卷积神经网络-最小二乘支持向量机的数据回归预测&#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现CNN-LSSVM基于卷积神经网络-最小二乘支持向量机的数据回归预测&#xff08;多指标&#xff0c;多图&#xff09;…

关于信息安全软考的记录6

1、入侵检测相关概念 及 入侵检测模型 入侵&#xff1a;违背访问目标的安全策略的行为 判断入侵的依据是&#xff1a;对目标的操作是否超出了目标的安全策略范围 入侵检测&#xff1a;通过收集操作系统、系统程序、应用程序、网络包等信息&#xff0c;发现系统中违背安全策略…

【unity】【VR】白马VR课堂系列-VR开发核心基础04-主体设置-XR Rig的引入和设置

接下来我们开始引入并构建XR Rig。 你可以将XR Rig理解为玩家在VR世界中的替身。 我们先删除Main Camera&#xff0c;在Hierarchy右键点击删除。 然后再在场景层右键选择XR下的XR Origin。这时一个XR Origin对象就被添加到了Hierarchy。 重设XR Origin的Position和Rotation…

C++中将十六进制数转化为字符串数据

C中将十六进制数转化为字符串数据 1、十六进制转字符串2、string转char[]3、调用4、调试结果 1、十六进制转字符串 std::string Number2HexStr( uint32_t mData ) {std::stringstream ss;ss << std::hex << std::setw(2) << std::setfill(0) << (int)…