C++11 --- 可变参数模板

news2024/11/12 7:47:39

序言

 不知道大家有没有细细研究过在 C 语言 中的 printf 函数,也许我们经常使用他,但是我们可能并不是那么了解他。先看一下调用格式:int printf ( const char * format, ... );,在这里的 format 代表我们的输出格式,后面的 ... 省略号这又是什么呢,这代表 可变参数,你可以传递任意数量的参数。这是怎么实现的呢?


1. C 中的可变参数

1.1 可变参数的概念

 可变参数是指在函数定义中,允许传入不定数量的参数的一种机制。在编程语言中,可变参数使得函数能够更加灵活地处理不同数量的输入。

1.2 实现可变参数

 在实现可变参数的函数之前,我们先认识几个函数:

stdarg.h 头文件

 为了处理可变参数,C 语言 标准库提供了 stdarg.h 头文件,它定义了一组宏来访问这些参数。这些宏包括:

  • va_list:一个类型,用于声明一个变量,该变量可以用来遍历函数的参数列表。
  • va_start(ap, last_arg):初始化 va_list 变量 aplast_arg 是最后一个固定参数的名字,ap 将用来遍历所有后续的可变参数。
  • va_arg(ap, type):返回 ap 指向的下一个参数,并将 ap 更新为指向下一个参数的指针。type 参数指定了期望的参数类型。
  • va_end(ap):清理 va_list 变量 ap,结束对可变参数列表的遍历。

现在我们实现一个简单的打印数字的可变参数函数:

void PrintNums(int cnt, ...)
{
	va_list ap;

	// 初始化
	va_start(ap, cnt);

	// 遍历
	for (int i = 0; i < cnt; i++)
	{
		// 遍历参数包中的所有参数
		int num = va_arg(ap, int);
		std::cout << num << ' ';
	}
	std::cout << std::endl;

	// 释放
	va_end(ap);
}


int main()
{
	// 第一个参数代表可变参数的个数
	PrintNums(5, 1, 2, 3, 4, 5);
	return 0;
}

最后的输出结果也和我们的预期一致:

1 2 3 4 5


1.3 可变参数的原理

 首先我们传递我们的参数时,是 从右到左依次入栈,如下图:
在这里插入图片描述
注意:在这里不要被图像误导,参数占的空间其实很小,只是为了美观画的大一点

通过查看 va_list 的定义 — typedef char* va_list; ,我们发现其实他就是一个 char* 指针。现在该指针需要指向可变参数的起始部分,所以我们需要传递 cnt 过去,对该指针进行初始化后自然就指向了可变参数的起始地址:
在这里插入图片描述
之后我们取数据的时候告诉指针,这是一个 int 类型,你一次性要取 4 / 8 个字节才是完整的数据。之后该指针一直重复取数据的参数,直至遇到结束条件(在这里是 i == cnt)。

 原理似乎也没有那么复杂,但是当可变参数遇到模板时…


2. C++ 中的可变参数模板

2.1 可变参数模板

 在 C++ 中,可变参数模板允许你定义可以 接受任意数量模板参数的模板函数或模板类。这是通过使用模板参数包(template parameter pack)和函数参数包(function parameter pack)来实现的。

2.2 实现可变参数模板

 可变参数模板实际使用起来是比较别扭的,比如这里我就简单实现一个打印多个类型的函数:

void MyPrint()
{
	std::cout << std::endl;
}

template <class T, class... Args>
void _MyPrint(const T &val, Args... args)
{
	std::cout << val << ' ';
	_MyPrint(args...);
}

template <class... Args>
void MyPrint(Args... args)
{
	_MyPrint(args...);
}


int main()
{
	MyPrint(1, 1.2, 'A', "ABCD");

	return 0;
}

输出结果也没有任何的问题:

1 1.2 A ABCD

这个方案的逻辑是递归的去解析参数包,每次取出一个参数直至参数取为空。

 接下来还有一个方案,实现的方式稍微简单一些:

template <class T>
int _MyPrint(const T& val)
{
	std::cout << val << " ";
	return 0;
}

template <class... Args>
void MyPrint(Args... args)
{
	int arr[] = {_MyPrint(args)...};
	std::cout << std::endl;
}

int main()
{
	MyPrint(1, 1.2, 'A', "ABCD");

	return 0;
}

当然结果肯定和方案一是一致的,但是我们又该怎么理解呢:

  1. 当我们编译程序时需要为这个数组申请指定大小的空间
  2. 但是怎么获取数组中有多少元素呢
  3. 数组中的函数执行一次就有一个返回值,所以执行多少次就有多少元素
  4. 那么函数具体执行多少次呢
  5. 该函数需要一个参数,所以参数包里的参数数量决定执行次数
  6. 执行该函数时我们就会处理一个参数直至参数被使用完

但是很少有让我们实现可变参数模板的场景,大家当作了解一下。


3. 可变参数模板的应用

 就拿容器 vector 举例子,它常使用 insert,push_back 这两个方法添加元素,为了提高效率通过可变参数模板,新的插入元素的方法 emplace, emplace_back 由此而生。

 他的怎么高效的呢?举个栗子来比较一下:

// 方式一
std::string s = "ABC";
vec.push_back(s);

// 方式二
vec.push_back(std::string("ABC"));

// 方式三
vec.emplace_back("ABC");

我们在这里来比较三者的效率:

  • 方案一:构造函数 + 拷贝构造
  • 方案二:构造函数 + 移动构造
  • 方案三:构造函数

emplace_back直接在容器内构造对象可以避免多余的复制或移动操作,提高性能。
并且 emplace_back 是兼容 push_back 的使用的,所以在使用时大家尽量使用前者。


4. 总结

 在这篇文中我们首先介绍了在 C 语言 中的可变参数,之后简单讲解了 C++ 中的可变参数模板的使用以及应用。

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

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

相关文章

若依库存管理 ruoyi-wms V2.0发布:升级到jdk17和vue3,支持一物一码

开源地址 https://gitee.com/zccbbg/wms-ruoyi 项目代码、文档 均开源免费可商用 遵循开源协议在项目中保留开源协议文件即可 活到老写到老 为兴趣而开源 为学习而开源 为让大家真正可以学到技术而开源 若依wms是一套基于若依的wms仓库管理系统&#xff0c;支持lodop和网页…

Windows环境虚拟机安装

一、软件安装 1. vmware官网地址 点进去选这个products 然后往下滑选这个查看桌面虚拟机管理程序 然后点击这个桌面虚拟机管理程序 然后下拉找到download now下载 然后会跳转到broadcom网站&#xff0c;选择注册账号,这里我是使用谷歌邮箱注册的 注册完之后点击这个链接&…

【LeetCode】07.整数反转

题目要求 解题思路 这道题的难点在于怎么判断越界&#xff0c;我们无法直接与最大值或最小值比较&#xff0c;但是由于每一次我们的ret都需要乘10这个特性来使用ret与最大值或最小值除10进行比较 代码实现 class Solution { public:int reverse(int x) {int ret0;while(x){…

JS生成二维码QRCode代码

JavaScript是一种广泛使用的前端编程语言&#xff0c;它不仅用于网页交互&#xff0c;还可以实现许多实用功能&#xff0c;如生成二维码。本篇文章将深入探讨如何使用JavaScript生成二维码&#xff0c;以及如何确保这种生成的二维码在各种浏览器和手机端都能正常工作&#xff0…

C语言-程序环境 #预处理 #编译 #汇编 #链接 #执行环境

文章目录 前言 一、程序的环境翻译和执行环境 二、翻译环境 (一)、整体把握 (一)、编译 1、预处理(预编译) 2、编译 a、词法分析 b、语法分析 c、语义分析 d、符号汇总 3、汇编 (二)、链接 三、运行环境 总结​​​​​​​ 前言 路漫漫其修远兮&#xff0c;吾将…

波导模式分析2 用于圆TE01模式高功率传输线的大型多模波导滤波器

摘要&#xff1a; 一种对于大型多模波导滤波器的设计方法&#xff0c;其能衰减掉&#xff08;deteriorate&#xff09;不想要的模式而不影响所需要的工作模式&#xff0c;被提出来抑制用于圆TE01模式高功率传输线的受限模式谐振。为了从TE10模式中分离出不期望的模式&#xff…

【蓝桥杯嵌入式(二)Led、Key、Lcd】

蓝桥杯嵌入式&#xff08;二&#xff09;Led、Key、Lcd 五、Led模块1.原理图配置2. 知识点3.底层代码 六、Key模块1.原理图配置2.知识点3.底层代码底层代码&#xff08;四⾏代码版本&#xff09;底层代码&#xff08;状态机版本&#xff09; 七、LCD模块1.原理图配置2.知识点底…

文章改写工具,帮你进行文章修改润色提升文章质量

在文字的世界里&#xff0c;每一篇文章都是创作者心灵的结晶。然而&#xff0c;即使是经验丰富的作家&#xff0c;也难免会在创作过程中遇到表达上的瓶颈。此时&#xff0c;文章改写工具便显得尤为重要&#xff0c;它以其独特的功能&#xff0c;对文章进行精准的修改与润色&…

机器学习算法那些事 | Plotly Express:一种简洁且强大的可视化神器

本文来源公众号“机器学习算法那些事”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;Plotly Express&#xff1a;一种简洁且强大的可视化神器 Plotly Express 是 Python 交互式库 Plotly 的高级组件&#xff0c;受 Seaborn 和 g…

全国机器人大赛 Robocon 常州工学院团队首战国三

全国机器人大赛 Robocon 常州工学院团队首战国三 通宵7天7夜&#xff0c;常州工学院RC团队&#xff0c;首次闯入全国机器人大赛国赛&#xff0c;并成功得分&#xff01; 不同于老牌强队&#xff0c;常州工学院&#xff08;下面用"常工"代替&#xff09;的这只队伍&…

Java题集(由入门到精通)03

此系列文章收录大量Java经典代码题&#xff08;也可以算是leetcode刷题指南&#xff09;&#xff0c;希望可以与大家一起努力学好Java。3、2、1&#xff0c;请看&#xff01; 目录 1.创建学生成绩表 2.冒泡排序 3.模拟彩票中奖 4.杨辉三角 1.创建学生成绩表 输入n个学生的…

【学习笔记】SSL证书安全机制之证书撤销

前言&#xff1a;以往提到过&#xff0c;钓鱼网站会仿冒我们&#xff0c;如果我们的私钥泄露了&#xff0c;如果被不法分子得到了私钥&#xff0c;他们就能假装是我们网站。那现在&#xff0c;我们要做的是生成新私钥并申请新证书。问题来了&#xff0c;旧的证书亦然存在且有效…

胶条的回弹状态检测 仅需一台回弹测试仪

胶条的回弹状态检测之所以重要&#xff0c;主要基于以下几个方面的考虑&#xff1a; 一、保证产品功能性和密封性 密封效果&#xff1a;胶条作为密封元件&#xff0c;其回弹性能直接关系到密封效果的好坏。 耐久性&#xff1a;如果回弹性能不佳&#xff0c;容易出现永久性变形&…

8个txt自动化脚本,一定有你用得上的!Python如何读取txt文件数据!

这次和大家分享txt办公自动化&#xff0c;包括读取、对比、过滤、合并、转换格式、提取数据、统计词频、生成报告等。 分享一份Python学习大礼包&#xff08;激活码安装包、Python web开发&#xff0c;Python爬虫&#xff0c;Python数据分析&#xff0c;人工智能、自动化办公等…

GAMES101(0~1作业)

搭建虚拟机环境 安装Oracle VM VirtualBox虚拟机&#xff0c;安装虚拟硬盘&#xff0c;配置Linux Ubuntu-64 bit系统&#xff0c;启动虚拟机&#xff0c;发生冲突错误&#xff1a; 将Vmware虚拟设备取消挂起状态&#xff0c;关机确保 Hyper-V 完全关闭&#xff1a;bcdedit /se…

Pandas DataFrame的多级列索引导出到Excel时,如何避免空白行和列

我想将multi-header数据框保存为Excel文件。以下是示例代码&#xff1a; import pandas as pd import numpy as npheader pd.MultiIndex.from_product([[location1,location2],[S1,S2,S3]],names[loc,S])df pd.DataFrame(np.random.randn(5, 6), index[a,b,c,d,e], columnsh…

Python 中的 11 种经典时间序列预测方法(备忘单)

摘要: 本文演示了 11 种不同的经典时间序列预测方法,这些方法包括: 自回归(AR) 移动平均线 (MA) 自回归移动平均线 (ARMA) 自回归综合移动平均线 (ARIMA) 季节性自回归综合移动平均线 (SARIMA) 季节性自回归综合移动平均线与外生回归量... 本文演示了 11 种不同的经典时间序…

sheng的学习笔记-AI-半监督聚类

AI目录&#xff1a;sheng的学习笔记-AI目录-CSDN博客 半监督学习&#xff1a;sheng的学习笔记-AI-半监督学习-CSDN博客 聚类&#xff1a;sheng的学习笔记-AI-聚类(Clustering)-CSDN博客 均值算法&#xff1a;sheng的学习笔记-AI-K均值算法_k均值算法怎么算迭代两次后的最大…

掌握Git分支管理策略:让团队协作更高效

在现代软件开发过程中&#xff0c;版本控制系统&#xff08;VCS&#xff09;是不可或缺的一部分。Git作为目前最流行的分布式版本控制系统之一&#xff0c;为开发者提供了强大的工具集来管理代码变更历史。然而&#xff0c;仅仅掌握Git的基本命令并不足以应对大型项目和团队协作…

当天审稿,当天上线,9月检索!

各领域CNKI知网普刊&#xff0c;最快一期预计下周送检&#xff0c;最快1天上线 领域广&#xff0c;计算机&#xff0c;社科&#xff0c;医学等各个方向都能收 包检索&#xff0c;可提供期刊部发票 知名出版社英文普刊 NO.1、Food Science and Nutrition Studies ISSN: 2573…