【C++11特性篇】模板的新一力将:可变参数模板 [全解析]

前言

大家好吖,欢迎来到 YY 滴C++系列 ,热烈欢迎! 本章主要内容面向接触过C++的老铁
主要内容含:在这里插入图片描述

欢迎订阅 YY滴C++专栏!更多干货持续更新!以下是传送门!

  • YY的《C++》专栏
  • YY的《C++11》专栏
  • YY的《Linux》专栏
  • YY的《数据结构》专栏
  • YY的《C语言基础》专栏
  • YY的《初学者易错点》专栏
  • YY的《小小知识点》专栏

目录

  • 一.引入:为什么printf可以支持多个参数的输入?————函数的可变参数
  • 二.可变参数模板
    • 【1】基本可变参数的函数模板演示:
    • 【2】使用:求函数包的大小——>【...语法】
    • 【3】使用:递归函数方式展开参数包(遍历/打印)演示:
    • 【4】使用注意点:参数包(遍历/打印)是不支持类似数组一样的遍历打印方式
    • 【5】使用:"逗号表达式"方式展开参数包(遍历/打印)演示:(看懂即可)
    • 【6】使用:一般(遍历/打印)展开参数包的最常用方式——>【...语法】
  • 三.【可变参数-模板】的优势:——>直接传包,直接构造
    • 【1】简易代码样例——>帮助理解原理
    • 【2】实际应用【empalce_back】&【push_back】对比
      • 【1】empalce_back和push_back函数接口的差异
      • 【2】empalce_back和push_back完成尾插的效率对比

一.引入:为什么printf可以支持多个参数的输入?————函数的可变参数

  • 在我们学习C语言的过程中,我们会发现printf支持如下图所示操作:
    在这里插入图片描述
  • 底层原理 是: 他会用一个数组把实参存起来,printf会依次访问数组
  • 函数的可变参数如下文档所示:
    在这里插入图片描述

二.可变参数模板

【1】基本可变参数的函数模板演示:

  • 下面的参数 args 前面有省略号,所以它就是一个 可变模版参数
  • 我们把 带省略号的参数称为“参数包” ,它里面包含了0到N(N>=0)个模板参数
  • 用可变模版参数的一个主要特点:我们无法直接获取参数包args中的每个参数的,只能通过展开参数包(遍历)的方式来获取参数包中的每个参数【可在第3小点查看详解】
  • 虽然 参数包的底层是 ——> 类似数组的形式存储 ,但是语法不支持使用args[i]这样方式获取可变参数【可在第4小点查看详解】
// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}

【2】使用:求函数包的大小——>【…语法】

  • 代码:sizeof...(args)
void ShowList(Args... args)
{
	cout << sizeof...(args) << endl;
}

【3】使用:递归函数方式展开参数包(遍历/打印)演示:

  • 如下面代码所示:要设计两个函数
  1. 结束条件的函数
  2. 递归函数

分析:

  • 我们可以发现,设计的_ShowList函数的参数是(T val, Args… args)
  • 我们可以这样理解 ,——> 它把参数包的 第一个 拿了出来当作参数T, 剩下的参数包 再整成另一个新的参数包args…
void _ShowList()
{
	// 结束条件的函数————传空
	cout << endl;
}

template <class T, class ...Args>
void _ShowList(T val, Args... args)
{
	cout << val << " ";
	_ShowList(args...);
}

//args代表0-N的参数包
template <class ...Args>
void CppPrint(Args... args)
{
	_ShowList(args...);
}

int main()
{
	CppPrint();
	CppPrint(1);
	CppPrint(1, 2);
	CppPrint(1, 2, 2.2);
	CppPrint(1, 2, 2.2, string("xxxx"));

	// ...

	return 0;
}

【4】使用注意点:参数包(遍历/打印)是不支持类似数组一样的遍历打印方式

  • 参数包不支持如下面代码所示,根据其底层是 类似数组的形式 ,下面代码是想利用数组的方式打印
template <class ...Args>
void ShowList(Args... args)
{
	cout << sizeof...(args) << endl;

	// 不支持这样打印
	for (size_t i = 0; i < sizeof...(args); i++)
	{
		cout << args[i] << endl;
	}
}

【5】使用:"逗号表达式"方式展开参数包(遍历/打印)演示:(看懂即可)

  • 我们知道逗号表达式会 按顺序执行逗号前面的表达式
  • 函数中的逗号表达式:(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;
}

【6】使用:一般(遍历/打印)展开参数包的最常用方式——>【…语法】

  • 用如下面代码所示构建数组即可:int a[] = { PrintArg(args)...};
void CppPrint()//单独讨论参数为空的清空
{
	cout << endl;
}

template <class T>
int PrintArg(T t)
{
	cout << t << " ";

	return 0;
}

//args代表0-N的参数包
template <class ...Args>
void CppPrint(Args... args)
{
	int a[] = { PrintArg(args)...};
	cout << endl;
}

int main()
{
	CppPrint();
	CppPrint(1);
	CppPrint(1, 2);
	CppPrint(1, 2, 2.2);
	CppPrint(1, 2, 2.2, string("xxxx"));

	return 0;
}

三.【可变参数-模板】的优势:——>直接传包,直接构造

【1】简易代码样例——>帮助理解原理

  • 先设计一个日期类如下所示:
class Date
{
public:
    Date(int year = 1, int month = 1, int day = 1)
        :_year(year)
        , _month(month)
        , _day(day)
    {
        cout << "Date构造" << endl;
    }

    Date(const Date& d)
        :_year(d._year)
        , _month(d._month)
        , _day(d._day)
    {
        cout << "Date拷贝构造" << endl;
    }

private:
    int _year;
    int _month;
    int _day;
};
  • 设计一个可变参数的函数模板Create如下所示:
  • Create函数 接收了传入的"参数包" ,再把参数包拿去构造Date对象,如下面代码所示:
  • 分别传参有p1,p2,p3,p4等等形式, 有缺省的地方,初始化列表中也会自动调用缺省值
  • 这里就体现了 模板调用可变参数的特点: 灵活
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, 1, 1);
	Date* p5 = Create(d);//拷贝构造

	return 0;
}

【2】实际应用【empalce_back】&【push_back】对比

【1】empalce_back和push_back函数接口的差异

  • 我们会发现,这两个函数都是实现尾插功能
  • 在C++11中,他们也都支持 万能引用
  • 他们最主要的 差异 :empalce系列函数中参数有——> 可变参数包
    在这里插入图片描述
    在这里插入图片描述

【2】empalce_back和push_back完成尾插的效率对比

  • 如下图所示
  • emplace系列支持传参数包,如图中所示,都是 直接进行构造
  • 而pushback函数,在C++98版本中还是传统的, 先构造再拷贝构造 (部分编译器可能会直接优化成拷贝构造)
  • pushback函数,在C++11版本中, 先拷贝构造再进行移动拷贝 (部分编译器可能会直接优化成移动拷贝)
  • 但总体而言,直接构造和移动构造在效率上差别不大在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

循环神经网络中的梯度消失或梯度爆炸问题产生原因分析(二)

上一篇中讨论了一般性的原则&#xff0c;这里我们具体讨论通过时间反向传播&#xff08;backpropagation through time&#xff0c;BPTT&#xff09;的细节。我们将展示目标函数对于所有模型参数的梯度计算方法。 出于简单的目的&#xff0c;我们以一个没有偏置参数的循环神经…

华为鸿蒙开发应用工程师:连接未来,创造无限可能

1. 引言 随着智能设备的不断普及和发展&#xff0c;华为鸿蒙作为下一代全场景分布式操作系统&#xff0c;为开发者提供了全新的应用开发平台。作为一名鸿蒙开发应用工程师&#xff0c;您将扮演连接未来的重要角色&#xff0c;为智能生活创造无限可能。 2. 深度参与全场景应用…

modbus异常错误码说明

异常错误码说明 其中物理离散量输入和输入寄存器只能有I/O系统提供的数据类型&#xff0c;即只能是由I/O系统改变离散量输入和输入寄存器的数值&#xff0c;而上位机程序不能改变的数据类型&#xff0c;在数据读写上表现为只读&#xff0c;而内部比特或者物理线圈和内部寄存器或…

Profinet转485modbus网关解决传输距离及数据丢失问题

Profinet转485modbus网关&#xff08;XD-MDPN100/2000&#xff09;是一款能够解决传输距离及数据丢失问题的设备。在现场应用中&#xff0c;数据的传输距离和数据丢失最为常见的问题。 在现场添加Profinet转485modbus网关&#xff08;XD-MDPN100/2000&#xff09;即可解决传输距…

BDD - Python Behave 入门

BDD - Python Behave 入门 Behave 是什么Behave 的主要特点和组成部分Behave 实践安装 BehaveBehave 项目目录结构创建项目创建 Feature 文件创建步骤定义文件 执行用例执行全部用例执行部分用例 生成报告生成 Json report生成 HTML 报告生成 Junit report生成 Cucumber report…

Modbus-TCP数据帧

Modbus-TCP基于4种报文类型 MODBUS 请求是客户机在网络上发送用来启动事务处理的报文MODBUS 指示是服务端接收的请求报文MODBUS 响应是服务器发送的响应信息MODBUS 证实是在客户端接收的响应信息 Modbus-TCP报文: 报文头MBAP MBAP为报文头&#xff0c;长度为7字节&#xff0c…

postman和Jmeter的区别

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

非隔离恒压ACDC稳压智能电源模块芯片推荐:SM7015

非隔离恒压ACDC稳压智能电源模块芯片是一种用于将交流&#xff08;AC&#xff09;电源转换为直流&#xff08;DC&#xff09;电源的集成电路。这种芯片具有恒压输出功能&#xff0c;能够保持输出电压的稳定&#xff0c;适用于各种需要直流电源的应用场景。 非隔离电源模块通常…

【华为数据之道学习笔记】6-5数据地图的核心价值

数据供应者与消费者之间往往存在一种矛盾&#xff1a;供应者做了大量的数据治理工作、提供了大量的数据&#xff0c;但数据消费者却仍然不满意&#xff0c;他们始终认为在使用数据之前存在两个重大困难。 1&#xff09;找数难 企业的数据分散存储在上千个数据库、上百万张物理表…

2024年,消费品零售企业如何规划大模型和数据技术落地?

导读&#xff1a;品牌商和零售商目前都在做2024年的规划&#xff0c;本次分享基于爱分析过往的研究&#xff0c;带来消费品零售行业2024年宏观趋势和方向&#xff0c;以及如何落地大模型和数据技术。 分享嘉宾&#xff5c;张扬 爱分析联合创始人兼首席分析师 内容来源于爱分析…

【算法刷题】Day22

文章目录 1. 按摩师题干&#xff1a;算法原理&#xff1a;&#xff08;dp&#xff09;1. 状态表示&#xff1a;2. 状态转移方程3. 初始化4. 填表顺序5. 返回值 代码&#xff1a; 2. 寻找数组的中心下标题干&#xff1a;算法原理&#xff1a;&#xff08;前缀和&#xff09;代码…

大数据处理与分析

掌握分布式并行编程框架MapReduce掌握基于内存的分布式计算框架Spark理解MapReduce的工作流程、Spark运行原理熟悉机器学习概念 一.MapReduce Hadoop MapReduce是一个软件框架&#xff0c;基于该框架能够容易地编写应用程序&#xff0c;这些应用程序能够运行在由上千个商用机器…

亚马逊品牌分析ABA功能有哪些?亚马逊选品的量化标准有哪些?——站斧浏览器

亚马逊品牌分析ABA功能有哪些&#xff1f; 1、品牌市场份额&#xff08;Share of Voice&#xff09; ABA提供了品牌在特定类别中市场份额的详细数据。这一模块帮助品牌所有者准确评估其品牌在整个市场中的竞争地位和表现。通过了解市场份额&#xff0c;品牌方可以制定更具针对…

2024年金三银四必备面试题之自动化测试面试题及答案大全

1.你如何用Selenium测试&#xff1f; SeleniumMavenTestNGJekins 2.如何解决问题&#xff1f; 先思考&#xff0c;然后百度&#xff0c;考虑网速、电脑配置等原因&#xff0c;这题主要看重解决问题的能力和思维。 3.你是怎么开发测试框架的&#xff1f; SeleniumMavenTestNGJ…

【接口测试】如何定位BUG的产生原因

我们从在日常功能测试过程中对UI的每一次操作说白了就是对一个或者多个接口的一次调用&#xff0c;接口的返回的内容(移动端一般为json)经过前端代码的处理最终展示在页面上。http接口是离我们最近的一层接口&#xff0c;web端和移动端所展示的数据就来自于这层&#xff0c;那么…

ARM作业1

汇编实现三个灯闪烁 汇编代码&#xff1a; .text .global _start _start: 设置GPIOE,GPIOF时钟使能LDR R0,0X50000A28 LDR R1,[R0] ORR R1,R1,#(0x3<<4) STR R1,[R0] 设置PE10,PF10,PE8为输出 LED1LDR R0,0X50006000LDR R1,[R0]ORR R1,R1,#(0X1<<20)BIC R1…

二值图像的游程编码

二值图像的游程编码是一种用于图像压缩和数据传输的有效方法&#xff0c;它能够显著减小图像文件的大小&#xff0c;同时保留图像的重要信息。本文将介绍二值图像的游程编码的原理、优势以及在实际应用中的作用。 一、什么是二值图像的游程编码&#xff1f; 二值图像是由黑白…

位运算:Leetcode137.只出现一次的数字(2)

题目描述&#xff1a; 给你一个整数数组 nums &#xff0c;除某个元素仅出现 一次 外&#xff0c;其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。 示例 1&#xff1a; 输入&#xff1a;nums [2,2,3,2] 输出&#xff1a;3示例 2&#xff1a; 输入&…

STM32的以太网外设+PHY(LAN8720)使用详解(2):硬件设计

0 工具准备 1.野火 stm32f407霸天虎开发板 2.LAN8720数据手册 3.STM32F4xx中文参考手册1 PHY&#xff08;LAN8720&#xff09;硬件配置 1.1 硬件配置引脚说明 在LAN8720上电或复位时会读取一些特定引脚的电平&#xff0c;根据电平来进行硬件配置。LAN8720的引脚分布如下&…

电子科大软件测试~第三次作业

第三次作业 第一题 采用JUnit软件测试框架进行测试程序编程&#xff0c;实现对下面java程序进行单元测试&#xff0c;找出其中缺陷。然后修改缺陷&#xff0c;直到通过单元测试&#xff0c;给出测试程序脚本和运行结果界面。 public class getMax {public int get_max(int x…