新闻详情

新闻详情

首页 / 资讯中心 / 详情

C++ 11:可变参数模板

发布时间:2026/9/30 2:28:09来源:尧图网络
C++ 11:可变参数模板
目录一.基本语法及原理二.包扩展三.emplace系列接口一.基本语法及原理C11 支持可变参数模板也就是说支持可变数量参数的函数模板和类模板可变数目的参数被称为参数包存在两种参数包模板参数包表示零或多个模板参数函数参数包表示零或多个函数参数。template class ...Args void Func(Args... args) {} template class ...Args void Func(Args... args) {} template class ...Args void Func(Args... args) {}我们用省略号来指出一个模板参数或函数参数表示一个包在模板参数列表中class... 或 typename... 指出接下来的参数表示零或多个类型列表在函数参数列表中类型名后面跟... 指出接下来表示零或多个形参对象列表函数参数包可以用左值引用或右值引用表示跟前面普通模板一样每个参数实例化时遵循引用折叠规则。可变参数模板的原理跟模板类似本质还是去实例化对应类型和个数的多个函数。这里我们可以使用 sizeof... 运算符去计算参数包中参数的个数。可变模板参数模板参数包C11 引入使用...声明可以接收任意数量、任意类型的模板参数一组打包在一起的类型集合称为参数包。templateclass ...Args // Args 就是【模板参数包】 void Func(Args... args) // args 是【函数参数包】 { }class ...Args/typename ...Args模板参数包存放一堆类型Args... args函数参数包存放一堆函数实参 符号...叫做省略号用来标识 “包”。区分两个概念模板参数包...Args→ 一堆类型函数参数包Args... args→ 一堆变量核心语法规则sizeof...(包名)用来获取参数包里参数的数量templateclass ...Args void Test(Args... args) { cout sizeof...(Args) endl; // 类型个数 cout sizeof...(args) endl; // 参数个数二者相等 }参数包不能直接遍历C11依靠递归展开拆包 C17新增折叠表达式简化展开编译器原理 可变模板不是一个能接收无限参数的单个函数编译器根据你传入参数的数量与类型实例化出多个不同签名的函数。Func(1,2,abc); Func(10); // 生成两份不同实例函数项目sizeofsizeof...作用求内存占用字节数求参数包内元素数量操作对象普通类型、变量、表达式模板参数包 / 函数参数包能不能用于可变参数以外✅ 随处可用❌ 只能配合参数包括号内能不能加...不能参数包名后面不能再加...返回值含义字节大小 (size_t)参数个数 (size_t)二.包扩展1. 定义包扩展使用...把参数包里所有元素展开逐个铺开。只有存在参数包模板参数包Args.../ 函数参数包args...时才能进行包扩展。编译器会把模式复制多份包中每一个元素对应一份。C还⽀持更复杂的包扩展直接将参数包依次展开依次作为实参给⼀个函数去处理。下面这种写法不对编译会报错为什么呢首先参数包是在编译时进行推导的但这种写法是在运行时相当于编译器走到这里会先编译出合适的类型再进行运行但下面是运行时的过程只有编译完才会运行编译都没进行这就会报错其次这是可变模板参数传过来的数据类型都不一样怎么能用同意容器来存放不同的数据类型// 递归出口 void Print(){} templateclass T, class ...Args void Print(T first, Args... args) { cout first ; Print(args...); // args... 包扩展递归调用 }调用Print(1,2,3)Print(args...)→Print(2,3)下一轮 →Print(3)下一轮 →Print()包扩展只有这个作用吗下面我们讲解重要作用三.emplace系列接口#includelist // emplace_back总体而言是更高效推荐以后使用emplace系列替代insert和push系列 int main() { listbit::string lt; // 传左值跟push_back一样走拷贝构造 bit::string s1(11111111111); lt.emplace_back(s1); cout ***************************** endl; // 右值跟push_back一样走移动构造 lt.emplace_back(move(s1)); cout ***************************** endl; // 直接把构造string参数包往下传直接用string参数包构造string // 这里达到的效果是push_back做不到的 lt.emplace_back(1111111111); cout ***************************** endl; listpairbit::string, int lt1; // 跟push_back一样 // 构造pair 拷贝/移动构造pair到list的节点中data上 pairbit::string, int kv(苹果, 1); lt1.emplace_back(kv); cout ***************************** endl; // 跟push_back一样 lt1.emplace_back(move(kv)); cout ***************************** endl; //////////////////////////////////////////////////////////////////// // 直接把构造pair参数包往下传直接用pair参数包构造pair // 这里达到的效果是push_back做不到的 lt1.emplace_back(苹果1, 1); cout ***************************** endl; return 0; }一、分 3 种调用场景对比重点场景 1传入已经存在的对象左值bit::string s1(11111); lt.emplace_back(s1);等价于push_back(s1)s1左值T 推导为bit::string原地调用拷贝构造函数创建容器内对象。场景 2传入右值std::movelt.emplace_back(move(s1));等价于push_back(std::move(s1))move(s1)是右值T 推导为bit::string原地调用移动构造函数。上面两种写法emplace_back和push_back性能表现基本一致。场景 3【emplace 独有的优势】直接传入构造参数包 ⭐// push_back 无法直接这样写 lt.emplace_back(1111111111); lt1.emplace_back(苹果1, 1);发生了什么字符串字面量11111、苹果1,1作为参数包完整传入emplace_back通过std::forwardArgs(args)...包扩展 完美转发直接在 list 节点的内存上调用对应构造函数✅不需要临时对象和 push_back 对比lt.push_back(1111111111); // push_back内部行为 // 1. 先用字面量构造一个临时 bit::string临时对象 // 2. 将临时对象移动构造进容器节点 // 存在一次多余临时对象构造析构对于pairbit::string,intlt1.emplace_back(苹果1, 1); // 直接原地调用 pair 的构造 pair(const char*, int)如果使用 push_backlt1.push_back(make_pair(苹果1,1)); // 需要先构造临时pair再移动进容器多一次开销二、关键总结当传入现成对象左值 / 右值emplace_back≈push_back没有明显优势。当直接传入构造所需参数列表emplace_back优势巨大零临时对象原地构造这是 push_back 无法实现的写法。底层技术栈串联你全部学过的内容 万能引用 → 引用折叠 → 可变参数模板参数包→包扩展→ std::forward 完美转发 → emplace 原地构造。三、面试标准问题Qemplace_back 和 push_back 的区别push_back接收已经构造完成的元素对象emplace_back接收构造对象所需的参数包原地直接构造emplace_back 可以避免产生临时对象减少拷贝 / 移动开销emplace_back 依靠可变参数模板、万能参数包、完美转发实现。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Git hooks目录缺失导致创建失败?排查与修复完整指南 2026/9/30 3:22:56

Git hooks目录缺失导致创建失败?排查与修复完整指南

开发环境里有些报错特别奇怪,光看字面意思容易让人一头雾水,比如我今天要聊的这个:缺少 .git/hooks 目录导致创建失败。 很多人头一回撞到它时,第一反应是"hooks 目录是什么时候丢的",第二反应是"重建它…

阅读更多 →
Docker镜像与容器的核心区别:概念、原理与实战排查 2026/9/30 3:22:43

Docker镜像与容器的核心区别:概念、原理与实战排查

刚开始接触 Docker 的人,十个有九个会纠结同一个问题:镜像 Images 和容器 Container,到底是不是同一个东西?为什么docker images看不到正在运行的服务,docker ps又看不到下载好的镜像?这两个词看起来像同义…

阅读更多 →
从文件清单到全文检索:构建个人知识库的深度搜索方案 2026/9/30 3:22:43

从文件清单到全文检索:构建个人知识库的深度搜索方案

1. 为什么“把文件收拾整齐”治标不治本:先重新定义盘点想在几千个文件里找一段只记得零散字句的旧文档,最抓狂的不是文件多,而是你根本不知道自己到底存了些什么、它们分布在哪些目录、哪个版本才是最新的。我之前也走过弯路:花一…

阅读更多 →
vi编辑器实战指南:模式原理、高频命令与服务器高效编辑技巧 2026/9/30 3:22:43

vi编辑器实战指南:模式原理、高频命令与服务器高效编辑技巧

1. 为什么今天还值得花时间学vi说句实话,在2025年还在教学vi,听起来确实有点像在教人用算盘。但如果你真的在命令行环境里工作过哪怕一个星期,你就会明白vi从来就没有退出过历史舞台,它只是安静地躺在每一台Linux服务器的某个角落…

阅读更多 →
分布式事务面试100题:从2PC到TCC,高频考点全解析 2026/9/30 3:22:43

分布式事务面试100题:从2PC到TCC,高频考点全解析

面试后端岗位这些年,我发现一个规律:候选人简历上写“精通微服务”的不少,但能把分布式事务讲透的,十个人里最多一两个。分布式事务相关的问题几乎必考,且面试官不会只满足于“2PC、TCC、Saga”这几个名词。最近我用 D…

阅读更多 →
快速矩阵乘法工程实践:Strassen算法落地指南 2026/9/30 3:22:43

快速矩阵乘法工程实践:Strassen算法落地指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉