c++可变参数包语法解析
本文介绍c可变参数包的相关用法定义模板参数和函数参数如下代码所示可以定义一个接受可变参数的函数template typename... Args void func(Args... args) { //函数体 } //如果args是右值引用需要这样写:Args...代码中的Args是一个模板形参,args是一个函数形参可以用类似与func(1,hello,21454,std::string{world});这样的方式调用这个函数获取参数包长度用size_t size sizeof...(args)或者size_t size sizeof...(Args)都可以获得参数包里的参数数量并且Args的长度是在编译期就确定的所以可以和constexpr以及if constexpr结合使用,后面我们会用到参数包展开这个非常重要,后面我们所有使用args的方式都需要理解这个假定args参数包里包含arg1,arg2,arg3三个参数,那么args...会被展开成arg1,arg2,arg3func(args...); //等价于func(arg1,arg2,arg3);不止如此,如果我们将args...替换成pattern...也同样可以展开(std::cout args )...//这个代码本身不合法! //相当于下面的代码 (std::cout arg1 ),(std::cout arg2 ),(std::cout arg3 )当然这样写是不合法的,这里只是告诉大家args...可以换成某个带args的pattern...,这样接下来的内容会好理解一点args的使用递归解包为了便于介绍,实现一个print_all函数,功能是打印输入的所有参数templatetypename T void print_all(T first){//这个重载是为了创造递归终点 std::cout first std::endl; } templatetypename T,typename... Args void print_all(T first,Args... args){ std::cout first std::endl; print_all(args...); }代码很好理解,每一层递归都负责打印参数包内第一个参数,并且提供一个重载作为递归终点,防止无限递归如果c标准比较新的话,也可以把上面那个重载去掉,用lambda实现递归调用,但是这样做没什么用,因为从c17开始就有更好的方式了,我把实现贴出来作为一个思路template typename... Args void print_all(Args... args) { auto print_single [](this auto self, auto arg_first, auto... args_tail) { std::cout arg_first ; if constexpr (sizeof...(args_tail) 0) { self(args_tail...); } }; print_single(args...); }在函数内部定义一个lambda表达式,使用c23的this auto语法可以很方便地做到递归调用,用auto作为参数类型的lambda表达式在底层本身就是模板,所以可以适配每一次调用的args_tail的长度都不同为了有一个递归终点,使用if constexpr在编译期就计算出后面的条件,只有一个参数的时候条件不满足,后面的self()调用不会生成,避免编译错误初始化列表从c11开始可以使用初始化列表的方式template typename... Args void print_all(Args... args) { int dummy[] {(std::cout args , 0)...}; }使用int dummy[] {}这种初始化列表定义数组的时候{}里面的参数是严格按照从左到右的顺序执行的(expr,0)使用了c语言逗号表达式,先执行逗号左边的表达式,后执行右边的表达式,最后将逗号右边的值作为返回值(std::cout args , 0)...运用了先前提到的pattern...所以上述代码会被展开为:int dummy[] {(std::cout arg1 , 0),(std::cout arg2 , 0),(std::cout arg3 , 0)};初始化列表里的每一个元素都是(std::cout arg , 0)这个逗号表达式会先执行左边的std::cout arg ,后将右边的0作为整个表达式的值存入数组实质上是用初始化列表来确保打印参数的顺序,用逗号表达式的副作用来实现打印,dummy[]数组本身没有意义折叠表达式c17加入了折叠表达式语法如下:(args op ...) - arg1 op (arg2 op arg3) (... op args) - (arg1 op arg2) op arg3 (args op ... op init) - arg1 op (arg2 op (arg3 op init)) (init op ... op args) - ((init op arg1) op arg2) op arg3op是运算符(-*/,||等),args是参数包,同样可以换成pattern,后两种用法中的init是初始值,主要和加减乘除之类的一起用,最外层的括号是语法的一部分,不能省略!template typename... Args void print_all(Args... args) { (..., (std::cout args )); }(..., (std::cout args ))中的op是一个逗号运算符,被展开成((std::cout arg1 ),(std::cout arg2 )),(std::cout arg3 ),按逗号运算符的特点,三个参数按顺序依次输出再举一个计算参数和的例子template typename... Args int sum_all(Args... args) { int sum 0; sum (sum ... args); return sum; }调用sum_all(1, 2, 3, 4)可得到答案10c20 lambda初始化捕获除开上面三种常规用法外,有时会需要将参数包完美转发进的其他可调用对象templatetypename F,typename... Args void func(F f,Args... args) { std::functionvoid() task std::bind(std::forwardF(f), std::forwardArgs(args)...); //调用task或者将task转移 }通常来说我们会像上面的代码这样做,但是随着lambda的功能逐渐完善,更灵活的lambda能逐渐替换掉bind函数的功能,如果需要将参数包完美转发进lambda表达式中,可以使用c20新加入的,专为lambda初始化捕获的语法,代码如下:templatetypename F,typename... Args void func(F f,Args... args) { std::functionvoid() task [f std::forwardF(f), ... args std::forwardArgs(args)]() { f(args...); }; //调用task或者将task转移 }... args std::forwardArgs(args)这种前缀的...语法相当于对参数包里的每一个参数都执行一次初始化捕获(这里是std::forward()也可以换成其他函数),再将捕获后的各个参数再打包成args总结在c17以上的环境,折叠表达式是最好的用法,简洁且高效需要将参数包转移到lambda内部的时候,通常来讲可以直接[args]或者[args]直接捕获,但是如果要保留移动语义实现完美转发或者需要其他的初始化措施的话,c20的[...argsstd::forwardArgs(args)]这种语法更高效如果环境没有c20,又需要完美转发,可以用auto args_tuple std::make_tuple(std::forwardArgs(args)...)然后将元组move进lambda表达式

相关新闻

用AI魔法让老旧视频焕发新生:Video2X视频增强全攻略

用AI魔法让老旧视频焕发新生:Video2X视频增强全攻略

用AI魔法让老旧视频焕发新生:Video2X视频增强全攻略 【免费下载链接】video2x A machine learning-based video super resolution and frame interpolation framework. Est. Hack the Valley II, 2018. 项目地址: https://gitcode.com/GitHub_Trending/vi/video2x…

2026/8/3 9:08:27阅读更多 →
MeteoInfo深度解析:气象GIS与科学计算一体化平台的架构奥秘

MeteoInfo深度解析:气象GIS与科学计算一体化平台的架构奥秘

MeteoInfo深度解析:气象GIS与科学计算一体化平台的架构奥秘 【免费下载链接】MeteoInfo MeteoInfo: GIS, scientific computation and visualization environment. 项目地址: https://gitcode.com/gh_mirrors/me/MeteoInfo 在气象数据分析的世界里&#xff0…

2026/8/3 9:08:27阅读更多 →
OSPF协议原理与多区域网络实验指南

OSPF协议原理与多区域网络实验指南

1. OSPF协议基础与实验价值OSPF(Open Shortest Path First)作为链路状态路由协议的典型代表,已经成为企业级网络的核心路由技术。不同于距离矢量协议,OSPF通过洪泛链路状态信息,让区域内所有路由器构建相同的拓扑数据库…

2026/8/3 9:08:27阅读更多 →
工业自动化植板控制系统架构与Halcon视觉集成实践

工业自动化植板控制系统架构与Halcon视觉集成实践

1. 项目概述:植板控制系统v2.1的技术架构 这个植板控制系统的最新版本采用了典型的工业自动化三层架构设计。底层通过西门子S7系列PLC实现设备控制,中间层用C#构建业务逻辑处理模块,顶层则整合Halcon机器视觉框架完成图像处理任务。我在汽车零…

2026/8/3 10:35:17阅读更多 →
如何在AnythingLLM中免费实现智能文档OCR文字识别

如何在AnythingLLM中免费实现智能文档OCR文字识别

如何在AnythingLLM中免费实现智能文档OCR文字识别 【免费下载链接】anything-llm Stop renting your intelligence. Own it with AnythingLLM. Everything you need for a powerful local-first agent experience 项目地址: https://gitcode.com/GitHub_Trending/an/anythin…

2026/8/3 10:35:17阅读更多 →
Java工程师简历优化:技术表达与项目量化实战

Java工程师简历优化:技术表达与项目量化实战

1. Java简历包装的核心价值解析 在当前的IT就业市场中,Java工程师岗位的竞争异常激烈。根据各大招聘平台的数据统计,平均每个Java开发岗位会收到200份简历,而HR筛选每份简历的时间通常不超过30秒。这种情况下,一份专业到位的Java技…

2026/8/3 10:35:17阅读更多 →
深入解析字节TRAE框架:构建可靠AI智能体的核心原理与实践

深入解析字节TRAE框架:构建可靠AI智能体的核心原理与实践

在实际 AI 应用开发中,我们经常遇到一个核心矛盾:如何将一个复杂的、多步骤的、需要调用外部工具或知识的任务,稳定、可靠地交给 AI 模型去执行?直接让大语言模型(LLM)一次性生成最终答案,对于简…

2026/8/3 10:35:17阅读更多 →
终极视频画质修复指南:3步用Video2X让老旧视频焕发新生

终极视频画质修复指南:3步用Video2X让老旧视频焕发新生

终极视频画质修复指南:3步用Video2X让老旧视频焕发新生 【免费下载链接】video2x A machine learning-based video super resolution and frame interpolation framework. Est. Hack the Valley II, 2018. 项目地址: https://gitcode.com/GitHub_Trending/vi/vide…

2026/8/3 10:35:17阅读更多 →
3分钟掌握WinAsar:Windows上最轻量的Electron asar文件管理终极指南

3分钟掌握WinAsar:Windows上最轻量的Electron asar文件管理终极指南

3分钟掌握WinAsar:Windows上最轻量的Electron asar文件管理终极指南 【免费下载链接】WinAsar Portable and lightweight GUI utility to pack and extract asar( Electron archive ) files, Only 551 KB! 项目地址: https://gitcode.com/gh_mirrors/wi/WinAsar …

2026/8/3 10:33:16阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/3 0:29:53阅读更多 →
限时公开!某头部SaaS公司内部AI模板工厂架构文档(含5类行业模板源码+性能压测报告)

限时公开!某头部SaaS公司内部AI模板工厂架构文档(含5类行业模板源码+性能压测报告)

更多请点击: https://intelliparadigm.com 第一章:AI模板批量生成的核心价值与落地全景 AI模板批量生成正从实验性工具演进为现代软件工程的关键基础设施。它通过语义理解、上下文感知与结构化约束,将重复性高、模式明确的代码/文档/配置生成…

2026/8/3 0:33:53阅读更多 →
如何快速找回消失的网页:Web Archives浏览器扩展终极指南

如何快速找回消失的网页:Web Archives浏览器扩展终极指南

如何快速找回消失的网页:Web Archives浏览器扩展终极指南 【免费下载链接】web-archives Browser extension for viewing archived and cached versions of web pages, available for Chrome, Edge and Safari 项目地址: https://gitcode.com/gh_mirrors/we/web-a…

2026/8/3 0:20:37阅读更多 →
3个让你工作效率翻倍的Umi-OCR实战技巧:免费离线文字识别完全指南

3个让你工作效率翻倍的Umi-OCR实战技巧:免费离线文字识别完全指南

3个让你工作效率翻倍的Umi-OCR实战技巧:免费离线文字识别完全指南 【免费下载链接】Umi-OCR OCR software, free and offline. 开源、免费的离线OCR软件。支持截屏/批量导入图片,PDF文档识别,排除水印/页眉页脚,扫描/生成二维码。…

2026/8/3 0:00:32阅读更多 →
[具身智能-181]:PC+服务器+具身机器人:构建具身智能从仿真到量产的闭环迭代混合架构

[具身智能-181]:PC+服务器+具身机器人:构建具身智能从仿真到量产的闭环迭代混合架构

PC服务器具身机器人:构建具身智能从仿真到量产的闭环迭代混合架构一、前言:具身智能需要“混合算力闭环系统”传统人工智能依赖云端静态数据集训练,不具备物理交互能力,无法适应真实世界的不确定性。具身智能(Embodied…

2026/8/3 0:00:32阅读更多 →
[具身智能-181]:大分布式通信模型对比:看懂为什么 DDS 是 ROS2 底层通信最优解

[具身智能-181]:大分布式通信模型对比:看懂为什么 DDS 是 ROS2 底层通信最优解

前言构建机器人、具身智能这类分布式实时系统,通信底座直接决定整套系统的实时性、容错性、组网能力。分布式领域长期存在 4 类经典通信架构:点对点模式、Broker 中间代理模式、广播模式、以数据为中心(DDS)模式。很多开发者疑惑&…

2026/8/3 0:00:32阅读更多 →
无损视频剪辑终极指南:如何实现快速高效的多媒体处理

无损视频剪辑终极指南:如何实现快速高效的多媒体处理

无损视频剪辑终极指南:如何实现快速高效的多媒体处理 【免费下载链接】lossless-cut The swiss army knife of lossless video/audio editing 项目地址: https://gitcode.com/gh_mirrors/lo/lossless-cut 在数字媒体创作领域,视频编辑处理的质量损…

2026/8/3 2:32:59阅读更多 →
AI辅助本科论文写作:8大工具评测与高效使用指南

AI辅助本科论文写作:8大工具评测与高效使用指南

1. 本科生论文写作的AI辅助现状本科毕业论文是每个大学生必须跨越的一道坎。记得我当年写论文时,光是文献检索就花了整整两周时间,打印的参考文献堆满了半个书桌。如今AI技术的发展为学术写作带来了革命性变化,合理使用这些工具可以节省80%以…

2026/8/3 2:33:01阅读更多 →
如何快速配置大麦自动抢票系统:从零开始搭建Python抢票助手

如何快速配置大麦自动抢票系统:从零开始搭建Python抢票助手

如何快速配置大麦自动抢票系统:从零开始搭建Python抢票助手 【免费下载链接】ticket-purchase 大麦自动抢票,支持人员、城市、日期场次、价格选择 项目地址: https://gitcode.com/GitHub_Trending/ti/ticket-purchase 还在为抢不到热门演唱会门票…

2026/8/3 2:33:04阅读更多 →