当前位置: 首页 > news >正文

【C++可变模板参数】

C++11 可变模板参数总结:搞懂参数包、包扩展和 emplace


1. 为什么 C++11 需要可变模板参数?

在 C++11 之前,如果我们想写一个“参数个数可变、参数类型也可变”的函数,基本只能靠:

  • 写很多重载
  • 或者用...(C 风格可变参数,类型不安全)

而可变模板参数(Variadic Templates)解决的是:

  • 参数个数可变
  • 参数类型可变
  • 编译期类型安全
  • 能和泛型、右值引用、完美转发组合

一句话:它是现代 C++ 泛型编程的基础设施。


2. 基础语法:模板参数包 & 函数参数包

2.1 模板参数包

template<class...Args>

Args代表“0 个或多个类型”。

2.2 函数参数包

template<class...Args>voidFunc(Args...args){}

args代表“0 个或多个函数参数”。

如果要支持完美转发,常见写法是:

template<class...Args>voidFunc(Args&&...args){}

3.sizeof...:统计参数包个数

#include<iostream>usingnamespacestd;template<class...Args>voidPrintCount(Args&&...args){cout<<sizeof...(args)<<endl;// 参数个数//sizeof...(Args) 也可统计}intmain(){doublex=2.2;PrintCount();// 0PrintCount(1);// 1PrintCount(1,string("xxxxx"));// 2PrintCount(1.1,string("xxxxx"),x);// 3}

4. 包扩展(最核心)

参数包不能像数组那样args[i]访问,能做的核心操作就是“展开(expand)”。

4.1 递归展开(C++11 经典写法)

#include<iostream>#include<string>usingnamespacestd;// 递归终止voidShowList(){cout<<endl;}// 每次取一个,剩下的继续递归template<classT,class...Args>voidShowList(T x,Args...args){cout<<x<<" ";ShowList(args...);}template<class...Args>voidPrint(Args...args){ShowList(args...);}intmain(){Print();Print(1);Print(1,string("hello"));Print(1,string("hello"),2.2);}

这段代码的本质:编译器会在编译期“展开”为多组具体函数调用。

4.2 模式扩展:Func(args...)不是唯一玩法

template<classT>constT&GetArg(constT&x){cout<<x<<" ";returnx;}template<class...Args>voidArguments(Args...args){}template<class...Args>voidPrint(Args...args){Arguments(GetArg(args)...);// 包扩展模式:对每个 args 套一层 GetArg}

关键理解:GetArg(args)...等价于
GetArg(arg1), GetArg(arg2), GetArg(arg3)...


5. 可变模板参数 + 完美转发(高频考点)

5.1 为什么要std::forward<Args>(args)...

因为:有名字的变量都是左值表达式
如果你只写args...,右值信息会丢失,可能触发错误重载或性能下降。

template<class...Args>voidWrapper(Args&&...args){Target(std::forward<Args>(args)...);// 保留原始值类别}

5.2 一句话区分

  • std::move(x):无脑转右值
  • std::forward<T>(x):按模板推导结果“有条件转发”

6. 真实价值场景:emplace系列接口

emplace_back/emplace本质是可变模板参数 + 完美转发。

template<class...Args>voidemplace_back(Args&&...args);template<class...Args>iteratoremplace(const_iterator pos,Args&&...args);

相比push_back

  • push_back往往是“先构造对象,再拷贝/移动进容器”
  • emplace_back可以“直接把构造参数传到底层,在容器内存上原地构造”

示例理解:

std::vector<std::pair<std::string,int>>v;v.emplace_back("apple",1);// 直接构造 pair<string,int>

这就是“参数包一路转发到节点/元素构造函数”的价值。


7. 可变模板参数常见坑

7.1 不能把参数包当数组用

错误思路:

// args[i] // 不支持

参数包不是运行时容器,而是编译期语法结构。

7.2 忘记递归终止函数

递归展开必须有“空包终止版本”,不然会无限模板实例化报错。

7.3 忘记完美转发导致右值退化

Args&&... args+ 下层调用foo(args...),会把右值当左值传下去。

7.4const对象即使move也不一定能移动

const T通常无法真正“偷资源”,很多时候仍走拷贝语义。

http://www.jsqmd.com/news/615871/

相关文章:

  • 【限时解密】某TOP3电商平台订单测试SOP文档(含23个自动化断言脚本+Postman集合+Mock规则库)
  • 2026冰雹车凹陷修复技术解析:从损伤评估到合规修复指南 - 优质品牌商家
  • 西门子1200 PLC的MODBUS_RTU轮询程序那些事儿
  • LeetCode 删除无效的括号:python 题解匆
  • 【Scala PyTorch深度学习】PyTorch On Scala系列课程 第一章 03 :张量基本操作【AI Infra 3.0】[PyTorch Scala 硕士研一课程]
  • 芯谷科技—D384X 系列电流模式PWM控制器
  • Zookeeper分布式协调
  • 智能家居中枢:OpenClaw+千问3.5-27B解析语音指令
  • Windows下OpenClaw安装指南:Qwen2.5-VL-7B图文模型一键调用
  • 支付签名验签失效,资金秒盗!PHP接口安全防护全链路拆解,从OpenSSL到HMAC-SHA256
  • 2026年SOD试剂盒市场:这些公司值得深入了解,elisa试剂盒使用说明书/白介素检测试剂盒,sod试剂盒品牌找哪家 - 品牌推荐师
  • Java应用转GraalVM静态镜像后GC停顿归零?不!真实生产数据揭示:未配置--enable-http、--enable-https导致堆外内存泄漏的致命陷阱
  • PHP微服务性能跃升47%的秘密:Swoole 5.0新特性深度适配教程(仅限首批内测开发者掌握)
  • 3小时搞定OpenClaw飞书机器人:Phi-3-mini-128k-instruct对话集成
  • OpenClaw学习路径:从Qwen3.5-9B-AWQ-4bit入门到开发复杂自动化流
  • 这本《大语言模型》直接封神,清华张亚勤盛赞“入门圣经”,A100集群训练日志全公开!
  • OpenClaw+Phi-3-vision-128k-instruct智能剪辑:视频关键帧提取与摘要生成
  • 通过 GitHub Actions 实现简单模型重新训练自动化
  • AI开发-python-langchain框架(--AI 直接生成并执行 Python 代码 )友
  • 小程序设置底部tabbar
  • OpenClaw自动化测试:Qwen2.5-VL-7B多模态任务稳定性验证
  • 2026年三角洲俱乐部3×3保险箱:守护私密空间的智能选择
  • 2026届学术党必备的AI科研助手横评
  • MySQL数据库从库只读模式怎么开启_修改read_only参数实操指南
  • OpenClaw+千问3.5-9B:个人内容助手从资料收集到草稿生成全流程
  • html怎么转rollup plugin html_Rollup如何通过插件处理HTML入口
  • OpenClaw个人知识库:Qwen3-32B+Obsidian自动化信息归档系统
  • sklearn.cluster.KMeans(n_clusters=8)
  • WorkBuddy的优势和劣势分别是什么?
  • PHP 8.9错误处理增强配置:从php.ini到Runtime::setErrorHandler()的7层防御链构建实战