C++可变模板参数详细讲解
先给你一句终极人话背下来这就是全部Args 你随便起的一个名字跟int a里的a一样想改成ABC、Params、Shit都行没有任何魔法。... 只有两个功能打包、拆包写在左边...Args→ 意思是打包“这是一堆类型我先装成一包”写在右边args...→ 意思是拆包“把刚才那一包一个一个拆出来用”我只盯着这段代码讲别的一概不提线程池里这段是你唯一要懂的templatetypename F, typename... Args void submit(F f, Args... args) { auto task std::bind(std::forwardF(f), std::forwardArgs(args)...); }我逐字、逐符号翻译成人话。第一行templatetypename F, typename... Args1.typename F就是我要接收一个 “要执行的函数 / 任务”给它起个名叫 F。2.typename... Args...在左边 →打包Args是包名, 代表着大量的各种各样的参数类型名整句翻译我还要接收 “一堆随便多少个、随便什么类型的参数”我把这一堆打包包名叫 Args“一堆” 可以是0 个1 个int2 个int string3 个int string double100 个都行这里的 ... 就是允许传 “一堆”而不是固定一个。你完全可以写成templatetypename F, typename... ABC void submit(F f, ABC... abc)功能一模一样Args就是个名字别把它当神仙。第二行void submit(F f, Args... args)1.F f对应上面的 F那个要执行的函数本体叫 f。2.Args... argsArgs上面打包的那 “一堆类型”...在左边 → 继续打包args给 “这一堆参数的本体” 起的名字整句翻译这是跟上面那包类型对应的、一堆真实的参数叫 args大白话Args 一包类型int、string、double...args 一包数值10、hello、3.14...第三行std::forwardArgs(args)...这里的...在最右边→拆包翻译把刚才打包好的args这一包参数一个一个拆出来挨个传给 std::bind。我给你看「编译器实际做了什么」你瞬间就懂你写一行通用代码编译器会根据你传的参数自动生成对应代码。例子 1你提交 1 个参数pool.submit(函数, 10);编译器看到后自动把你的模板展开成templatetypename F, typename... Args // Args 被自动推导为int void submit(F f, int args) { auto task std::bind(..., std::forwardint(args)); }例子 2你提交 2 个参数pool.submit(Calculate(), 5, square);编译器自动展开成templatetypename F, typename... Args // Args 被自动推导为int, const char* void submit(F f, int args1, const char* args2) { auto task std::bind( ..., std::forwardint(args1), std::forwardconst char*(args2) ); }例子 3你提交 3 个参数pool.submit(func, 1, 2.2, abc);编译器自动展开成templatetypename F, typename... Args // Args int, double, const char* void submit(F f, int a1, double a2, const char* a3) { auto task std::bind( ..., std::forwardint(a1), std::forwarddouble(a2), std::forwardconst char*(a3) ); }现在告诉你为什么要写这玩意如果不写...你必须给1 个参数、2 个参数、3 个参数... 各写一个函数// 不支持可变参你要写死人 void submit(F f) { ... } void submit(F f, A1 a1) { ... } void submit(F f, A1 a1, A2 a2) { ... } void submit(F f, A1 a1, A2 a2, A3 a3) { ... }写了typename... ArgsArgs... argsargs...你写 1 行编译器自动帮你写 1000 行重载。这就是它唯一的作用。我再把三个 ... 汇总用最蠢的话再说一遍只看这三句记住就毕业typename... Args→我要收一堆类型打包包名叫 ArgsArgs... args→我要收一堆对应的值打包包名叫 argsargs...→把 args 这一包拆成一个一个挨个用C 把 **“打包” 和 “拆包” 用同一个符号...表示 **写在左边是打包写在右边是拆包长得一模一样所以看起来很复杂;这不是你的问题是 C 语法设计得反人类。最后给你一个 “傻瓜版写法”你以后照抄就行你以后写任意 “接收随便多少参数” 的函数固定照抄这个模板templatetypename F, typename... 随便起个名 void 函数名(F f, 随便起个名... 随便起个值名) { 用到参数的地方写随便起个值名... }放到线程池就是templatetypename F, typename... Args void submit(F f, Args... args) { auto task std::bind(std::forwardF(f), std::forwardArgs(args)...); }C11递归式调用示例 1基础版 —— 打印任意数量、任意类型的参数无返回值这是最直观的例子能清晰看到参数包的定义和展开逻辑#include iostream #include string // 步骤1定义「递归终止函数」必须参数包拆空时调用 // 作用当参数包没有参数时结束递归 void print_all() { std::cout \n参数打印完成\n; } // 步骤2定义可变参数模板函数核心 // T第一个参数的类型Args... rest剩余参数的参数包 templatetypename T, typename... Args void print_all(T first, Args... rest) { // 1. 处理第一个参数类型安全编译器知道T的类型 std::cout 参数 first | 类型 typeid(T).name() ; // 2. 递归调用把剩余参数包传进去逐步拆解 print_all(rest...); // rest... 是参数包展开拆成单个参数 } // 测试传任意数量、任意类型的参数 int main() { // 测试1传int、string、double、bool print_all(100, std::string(hello), 3.14, true); // 测试2传0个参数触发递归终止函数 print_all(); // 测试3传自定义类型可变参数模板的优势——支持任意类型 struct Person { std::string name; int age; }; // 给Person重载才能打印 std::ostream operator(std::ostream os, const Person p) { return os Person( p.name , p.age ); } print_all(Person{张三, 20}, Person{李四, 25}); return 0; }输出示例类型名因编译器不同略有差异参数100 | 类型int 参数hello | 类型basic_stringchar,... 参数3.14 | 类型double 参数1 | 类型bool 参数打印完成 参数打印完成 参数Person(张三,20) | 类型Person 参数Person(李四,25) | 类型Person 参数打印完成核心语法解释templatetypename T, typename... Args定义模板参数包Args表示 “任意数量的类型”void print_all(T first, Args... rest)函数参数包first是第一个参数rest是剩余参数的集合print_all(rest...)参数包展开把rest拆成单个参数传给下一次递归递归终止函数print_all()参数包拆空时必须有这个函数否则编译器会报错递归无法终止。示例 2进阶版 —— 求和任意数量的数值型参数有返回值展示如何处理 “有返回值 类型限制” 的场景比如只允许数值类型#include iostream #include type_traits // 用于类型检查 // 递归终止0个参数时返回0 templatetypename T int constexpr T sum_all() { return 0; } // 可变参数模板求和任意数值类型参数 templatetypename T, typename... Args auto sum_all(T first, Args... rest) - std::enable_if_tstd::is_arithmetic_vT, decltype(first) { // 静态断言确保所有参数都是数值类型int/double/float等 static_assert((std::is_arithmetic_vArgs ...), 所有参数必须是数值类型); // 递归求和第一个参数 剩余参数的和 return first sum_all(rest...); } int main() { // 测试1int double std::cout 12.53 sum_all(1, 2.5, 3) std::endl; // 6.5 // 测试2仅1个参数 std::cout 100 sum_all(100) std::endl; // 100 // 测试3错误示例传字符串触发静态断言 // std::cout sum_all(1, hello) std::endl; // 编译报错所有参数必须是数值类型 return 0; }C17 折叠表达式无递归推荐折叠表达式是 C17 专门为参数包设计的语法能直接 “展开” 参数包并执行批量操作比如打印、求和完全不需要递归。示例 3无递归打印任意参数最直观#include iostream #include string // 无递归折叠表达式直接展开参数包 templatetypename... Args void print_all(Args... args) { // 折叠表达式核心(操作, ...) 或 (... , 操作) // 这里是对每个参数执行 cout args 用逗号分隔 (std::cout ... args) | 打印完成\n; } int main() { // 测试任意参数 print_all(100, std::string(hello), 3.14, true); print_all(); // 0个参数也能处理折叠表达式空展开无输出 return 0; }输出100hello3.141 | 打印完成 | 打印完成折叠表达式核心语法解释(std::cout ... args)是二元左折叠等价于// 如果传 100, hello, 3.14 → 展开为 std::cout 100 hello 3.14;折叠表达式的通用格式左折叠(init op ... op args)→ 从左到右展开比如(a ... args)右折叠(args op ... op init)→ 从右到左展开空折叠参数包为空时数值类型返回 0逻辑类型返回 true流操作无输出安全。示例 2无递归求和任意数值参数#include iostream #include type_traits // 无递归求和折叠表达式 类型检查 templatetypename... Args auto sum_all(Args... args) { // 静态断言确保所有参数都是数值类型 static_assert((std::is_arithmetic_vArgs ...), 必须传数值类型); // 折叠表达式求和1 2 3 → (1 (2 3)) return (... args); } int main() { std::cout 12.53 sum_all(1, 2.5, 3) std::endl; // 6.5 std::cout 仅1个参数 sum_all(100) std::endl; // 100 std::cout 0个参数 sum_all() std::endl; // 0空折叠默认值 return 0; }示例 3无递归处理复杂逻辑比如每个参数执行自定义操作如果需要对每个参数执行多步操作比如打印值 类型可以用「折叠表达式 lambda」#include iostream #include string #include typeinfo templatetypename... Args void print_with_type(Args... args) { // 对每个参数执行lambda用逗号折叠执行多个操作 ( [](auto arg) { std::cout 值 arg | 类型 typeid(arg).name() \n; }(std::forwardArgs(args)), ... ); } int main() { print_with_type(100, std::string(hello), 3.14); return 0; }输出类型名因编译器略有差异值100 | 类型int 值hello | 类型basic_stringchar,... 值3.14 | 类型double递归 vs 折叠表达式 对比方式C 版本代码简洁度可读性效率适用场景递归C11中等中等高兼容旧编译器、复杂逻辑折叠表达式C17极高极高高现代编译器、简单 / 复杂逻辑

相关新闻

Vue Day3

Vue Day3

一.生命周期四个阶段:关注1.2阶段案例:createdmounted二.小黑记账清单列表渲染(请求)/添加/删除/饼图渲染三.工程化开发入门工程化开发和脚手架项目运行流程:组件化:组件注册:局部:A…

2026/7/3 11:08:38 阅读更多 →
【图像处理相关毕设选题选题指导】2026新颖优质选题推荐

【图像处理相关毕设选题选题指导】2026新颖优质选题推荐

目录 前言毕设选题更多帮助选题迷茫选题的重要性最后前言 📅大四是整个大学期间最忙碌的时光,一边要忙着备考或实习为毕业后面临的就业升学做准备,一边要为毕业设计耗费大量精力。近几年各个学校要求的毕设项目越来越难,有不少课题是研究生级别难度的,对本科同学来说…

2026/7/2 23:00:50 阅读更多 →
高性能日志库C++实现

高性能日志库C++实现

1、非修改序列算法这些算法不会改变它们所操作的容器中的元素。1.1 find 和 find_iffind(begin, end, value):查找第一个等于 value 的元素,返回迭代器(未找到返回 end)。find_if(begin, end, predicate):查找第一个满…

2026/7/2 23:36:36 阅读更多 →

最新新闻

界面控件DevExpress WinForms v26.1新版亮点 - 皮肤更新、面板功能增强

界面控件DevExpress WinForms v26.1新版亮点 - 皮肤更新、面板功能增强

DevExpress WinForms控件包含了190多个Windows Forms控件和UI库,能帮助开发者提供为Windows Forms平台创建具有强大影响力的软件解决方案所需的组件,最新版本支持.NET 10。 在接下来的系列文章中,我将为大家一一介绍DevExpress WinForms v26…

2026/7/3 11:43:52 阅读更多 →
Kimi-K2.5本地部署全指南:MoE大模型在24GB显存上的硬核落地

Kimi-K2.5本地部署全指南:MoE大模型在24GB显存上的硬核落地

1. 项目概述:当SOTA级大模型真正“落进”你的硬盘里Kimi-K2.5本地部署这件事,我从去年底第一次在Hugging Face上看到unsloth/Kimi-K2.5-GGUF仓库时就盯上了。不是因为标题里写的“24G显存可跑”有多吸睛,而是因为它背后那个被反复验证却极少落…

2026/7/3 11:43:52 阅读更多 →
Sunshine游戏串流完整指南:从零开始搭建你的私人云游戏平台

Sunshine游戏串流完整指南:从零开始搭建你的私人云游戏平台

Sunshine游戏串流完整指南:从零开始搭建你的私人云游戏平台 【免费下载链接】Sunshine Self-hosted game stream host for Moonlight. 项目地址: https://gitcode.com/GitHub_Trending/su/Sunshine Sunshine是一款开源免费的自托管游戏串流服务器&#xff0c…

2026/7/3 11:41:52 阅读更多 →
2026年桌面风扇推荐:别被参数迷惑,选对适合自己使用习惯的才明智

2026年桌面风扇推荐:别被参数迷惑,选对适合自己使用习惯的才明智

2026年桌面风扇推荐:别被参数迷惑,选对适合自己使用习惯的才明智2026年夏季,桌面风扇市场产品丰富,但不少消费者在“桌面风扇推荐2026”相关搜索中看到各种参数却不知如何对应实际需求。选购的核心不是看哪个指标最高,…

2026/7/3 11:41:52 阅读更多 →
冠宇仪器中标快检项目:盐都区农贸市场试剂采购彰显技术实力

冠宇仪器中标快检项目:盐都区农贸市场试剂采购彰显技术实力

近日,冠宇仪器制造(江苏)有限公司成功中标盐城市盐都区市场监督管理局农贸市场快检室试剂采购项目的消息,在食品安全快检行业引发广泛关注。企业凭借过硬的产品性能、全流程闭环服务体系和高性价比的落地方案脱颖而出,…

2026/7/3 11:39:50 阅读更多 →
在GEO优化中,是否应当优先考虑内容的视觉呈现?

在GEO优化中,是否应当优先考虑内容的视觉呈现?

随着生成式AI日益成为信息获取的重要渠道,GEO(生成式引擎优化)正悄然重塑品牌的数字曝光逻辑。在这场以内容质量为核心的角逐中,一个核心矛盾浮出水面:精心雕琢的文字,是否真的需要依赖夺目的视觉元素来“开…

2026/7/3 11:37:50 阅读更多 →

日新闻

Nginx防御TLS重协商攻击实战:从原理到配置与监控

Nginx防御TLS重协商攻击实战:从原理到配置与监控

1. 项目概述:为什么TLS重协商攻击至今仍需警惕十多年前的CVE-2011-1473,一个关于TLS/SSL协议重协商机制的漏洞,现在提起来还有必要吗?很多运维和开发朋友可能会觉得,这都老掉牙了,现代服务器和客户端不都默…

2026/7/3 0:03:59 阅读更多 →
华为防火墙双通道远程管理实战:Web与SSH配置详解

华为防火墙双通道远程管理实战:Web与SSH配置详解

1. 项目概述:为什么需要双通道远程管理防火墙?在任何一个稍具规模的企业网络里,防火墙都是那个默默守护在边界的关键角色。作为网络工程师,我们不可能每次都跑到机房,插上console线去配置它。远程管理能力,…

2026/7/3 0:03:59 阅读更多 →
AD74413R与PIC18F65K40的高精度工业数据采集方案

AD74413R与PIC18F65K40的高精度工业数据采集方案

1. 项目概述:AD74413R与PIC18F65K40的协同工作在工业自动化和精密测量领域,同时实现高精度模数转换(ADC)和数模转换(DAC)功能是许多复杂系统的核心需求。AD74413R作为一款四通道可配置模拟输入/输出器件,与PIC18F65K40微控制器的组合&#xf…

2026/7/3 0:05:59 阅读更多 →

周新闻

月新闻