C++11:可变参数模板
上篇文章C11:右值引用和移动语义详解目录1.基本语法及原理2.包扩展C17对包扩展的优化3.emplace系列接口4.在list中实现emplace1.基本语法及原理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...运算符去计算参数包中参数的个数。template class ...Args void Print(Args... args) { cout sizeof...(args) endl; } int main() { double x 2.2; Print(); // 包⾥有0个参数 Print(1); // 包⾥有1个参数 Print(1, string(xxxxx)); // 包⾥有2个参数 Print(1.1, string(xxxxx), x); // 包⾥有3个参数 return 0; }结果原理// 原理1编译本质这⾥会结合引⽤折叠规则实例化出以下四个函数 void Print(); void Print(int arg1); void Print(int arg1, string arg2); void Print(double arg1, string arg2, double arg3); // 原理2更本质去看没有可变参数模板我们实现出这样的多个函数模板才能⽀持 // 这⾥的功能有了可变参数模板我们进⼀步被解放他是类型泛化基础 // 上叠加数量变化让我们泛型编程更灵活。 void Print(); template class T1 void Print(T1 arg1); template class T1, class T2 void Print(T1 arg1, T2 arg2); template class T1, class T2, class T3 void Print(T1 arg1, T2 arg2, T3 arg3);2.包扩展对于一个参数包我们除了能计算他的参数个数我们能做的唯一的事情就是扩展它当扩展一个包时我们还要提供用于每个扩展元素的模式扩展一个包就是将它分解为构成的元素对每个元素应用模式获得扩展后的列表。我们通过在模式的右边放一个省略号.)来触发扩展操作。底层的实现细节如下图所示。C还支持更复杂的包扩展直接将参数包依次展开依次作为实参给一个函数去处理。void ShowList() { // 编译器时递归的终⽌条件参数包是0个时直接匹配这个函数 cout endl; } templateclass T, class ...Args void ShowList(T x, Args... args) { cout x ; // args是N个参数的参数包 // 调⽤ShowList参数包的第⼀个传给x剩下N-1传给第⼆个参数包 ShowList(args...); } // 编译时递归推导解析参数 templateclass ...Args void Print(Args... args) { ShowList(args...); } int main() { Print(); Print(1); Print(1, string(xxxxx)); Print(1, string(xxxxx), 2.2); return 0; }结果原理// 本质编译器将可变参数模板通过模式的包扩展编译器推导的以下三个重载函数 void ShowList(double x) { cout x ; ShowList(); } void ShowList(string x, double z) { cout x ; ShowList(z); } void ShowList(int x, string y, double z) { cout x ; ShowList(y, z); } void Print(int x, string y, double z) { ShowList(x, y, z); }template class T const T GetArg(const T x) { cout x ; return x; } template class ...Args void Arguments(Args... args) { } template class ...Args void Print(Args... args) { // 注意GetArg必须返回或者到的对象这样才能组成参数包给Arguments Arguments(GetArg(args)...); } // 本质可以理解为编译器编译时包的扩展模式 // 将上面的函数模板扩展实例化为下面的函数 //void Print(int x, string y, double z) //{ // Arguments(GetArg(x), GetArg(y), GetArg(z)); //} int main() { Print(1, string(xxxxx), 2.2); return 0; }C17对包扩展的优化// 折叠式表达 C17 template class ...Args void Print(Args... args) { ((cout args ), ...); cout \n; } int main() { Print(); Print(1); Print(1, string(xxxxx)); Print(1, string(xxxxx), 2.2); return 0; }实例化为Print(1, string(xxxxx), 2.2); // 实例化为下面的函数 void Print(int x, string y, double z) { //((cout args ), ...); ((cout x ), (cout y ), (cout z )); cout \n; }3.emplace系列接口template class... Args void emplace_back (Args... args); template class... Args iterator emplace (const_iterator position, Args... args);C11以后STL容器新增了empalce系列的接口empalce系列的接口均为模板可变参数功能上兼容push和insert系列但是empalce还支持新玩法假设容器为containerTempalce还支持直接插入构造T对象的参数这样有些场景会更高效一些可以直接在容器空间上构造T对象。emplace_back总体而言是更高效推荐以后使用emplace系列替代insert和push系列第二个程序中我们模拟实现了list的emplace和emplace_back接口这里把参数包不段往下传递最终在结点的构造中直接去匹配容器存储的数据类型T的构造所以达到了前面说的empalce支持直接插入构造T对象的参数这样有些场景会更高效一些可以直接在容器空间上构造T对象。传递参数包过程中如果是 Args... args的参数包要用完美转发参数包方式如下std::forwardArgs(args)... 否则编译时包扩展后右值引用变量表达式就变成了左值。// emplace_back总体而言是更高效推荐以后使用emplace系列替代insert和push系列 int main() { listxxx::string lt; // 传左值与push_back一样走拷贝构造 xxx::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(111111111111); cout ********************************* endl; return 0; }结果int main() { listpairxxx::string, int lt1; // 跟push_back⼀样 // 构造pair 拷⻉/移动构造pair到list的节点中data上 pairxxx::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); cout ********************************* endl; return 0; }结果4.在list中实现emplace// List.h namespace xxx { templateclass T struct ListNode { ListNodeT* _next; ListNodeT* _prev; T _data; ListNode(T data) :_next(nullptr) , _prev(nullptr) , _data(move(data)) { } template class... Args ListNode(Args... args) : _next(nullptr) , _prev(nullptr) , _data(std::forwardArgs(args)...) { } }; templateclass T, class Ref, class Ptr struct ListIterator { typedef ListNodeT Node; typedef ListIteratorT, Ref, Ptr Self; Node* _node; ListIterator(Node* node) :_node(node) { } // it; Self operator() { _node _node-_next; return *this; } Self operator--() { _node _node-_prev; return *this; } Ref operator*() { return _node-_data; } bool operator!(const Self it) { return _node ! it._node; } }; templateclass T class list { typedef ListNodeT Node; public: typedef ListIteratorT, T, T* iterator; typedef ListIteratorT, const T, const T* const_iterator; iterator begin() { return iterator(_head-_next); } iterator end() { return iterator(_head); } void empty_init() { _head new Node(); _head-_next _head; _head-_prev _head; } list() { empty_init(); } void push_back(const T x) { insert(end(), x); } void push_back(T x) { insert(end(), move(x)); } iterator insert(iterator pos, const T x) { Node* cur pos._node; Node* newnode new Node(x); Node* prev cur-_prev; // prev newnode cur prev-_next newnode; newnode-_prev prev; newnode-_next cur; cur-_prev newnode; return iterator(newnode); } iterator insert(iterator pos, T x) { Node * cur pos._node; Node* newnode new Node(move(x)); Node* prev cur-_prev; // prev newnode cur prev-_next newnode; newnode-_prev prev; newnode-_next cur; cur-_prev newnode; return iterator(newnode); } template class... Args void emplace_back(Args... args) { insert(end(), std::forwardArgs(args)...); } // 原理本质编译器根据可变参数模板⽣成对应参数的函数 /*void emplace_back(string s) { insert(end(), std::forwardstring(s)); } void emplace_back(string s) { insert(end(), std::forwardstring(s)); } void emplace_back(const char* s) { insert(end(), std::forwardconst char*(s)); } */ template class... Args iterator insert(iterator pos, Args... args) { Node* cur pos._node; Node* newnode new Node(std::forwardArgs(args)...); Node* prev cur-_prev; // prev newnode cur prev-_next newnode; newnode-_prev prev; newnode-_next cur; cur-_prev newnode; return iterator(newnode); } private: Node* _head; }; }本章完。

相关新闻

GME-Qwen2-VL-2B部署教程:支持中文语义的图文联合向量服务快速上线

GME-Qwen2-VL-2B部署教程:支持中文语义的图文联合向量服务快速上线

GME-Qwen2-VL-2B部署教程:支持中文语义的图文联合向量服务快速上线 本文介绍如何快速部署GME-Qwen2-VL-2B多模态向量模型,这是一个支持中文语义理解的图文联合向量服务,能够处理文本、图像和图文对输入,生成统一的向量表示。 1. 环…

2026/7/5 11:05:26 阅读更多 →
一个好的接口自动化测试脚本是怎么写出来的?

一个好的接口自动化测试脚本是怎么写出来的?

前言 谈到接口测试,大家关注更多的是哪个工具更优秀,更好用。但是很少人关注到接口测试用例的设计问题,也很少人会去写接口用例,都代码化了嘛,还写什么用例,是吧? 这样真的对么?我们…

2026/5/17 6:25:26 阅读更多 →
CosyVoice语音生成大模型-300M-25Hz部署详解:Win10镜像环境下Docker容器化部署指南

CosyVoice语音生成大模型-300M-25Hz部署详解:Win10镜像环境下Docker容器化部署指南

CosyVoice语音生成大模型-300M-25Hz部署详解:Win10镜像环境下Docker容器化部署指南 你是不是也对AI语音合成感兴趣,想自己动手部署一个模型来玩玩?今天,我就来手把手带你,在Windows 10系统上,用Docker容器…

2026/5/17 9:20:11 阅读更多 →

最新新闻

NAS / 服务器断电数据丢失?UPS 配套 USB关机保护板,市电断电池低自动提示服务器/电脑关机

NAS / 服务器断电数据丢失?UPS 配套 USB关机保护板,市电断电池低自动提示服务器/电脑关机

20年UPS监控产品研发,专注于UPS机房,电池巡检,环境监测等领域,提供专业,稳定可靠的应用方案和成熟的系列产品。 20年UPS监控产品研发,专注于UPS机房,电池巡检,环境监测等领域&#x…

2026/7/8 13:47:35 阅读更多 →
TLP241A光耦与GD32VF103VBT6在工业隔离设计中的应用

TLP241A光耦与GD32VF103VBT6在工业隔离设计中的应用

1. 项目背景与核心需求在工业控制和电力电子系统中,电气隔离是一个至关重要的设计考量。TLP241A光耦与GD32VF103VBT6微控制器的组合,为解决高噪声环境下的信号传输问题提供了可靠方案。这个设计主要应对三个核心挑战:安全隔离:防止…

2026/7/8 13:45:35 阅读更多 →
5分钟极速方案:用OneDragon重构你的《绝区零》游戏体验

5分钟极速方案:用OneDragon重构你的《绝区零》游戏体验

5分钟极速方案:用OneDragon重构你的《绝区零》游戏体验 【免费下载链接】ZenlessZoneZero-OneDragon 绝区零 一条龙 | 全自动 | 自动闪避 | 自动每日 | 自动空洞 | 支持手柄 项目地址: https://gitcode.com/gh_mirrors/ze/ZenlessZoneZero-OneDragon 当你打开…

2026/7/8 13:45:35 阅读更多 →
6.LVM动态扩容

6.LVM动态扩容

1.如图,提前在虚拟机中添加了三块NVMe硬盘2.使用以下命令创建vg并为创建的卷组添加pv,并查看效果vgcreate [vg名称] [块设备文件]:创建vg并为创建的卷组添加pvpvscan:查看pv信息vgdisplay [vg]:查看指定vg信息3.为vg01…

2026/7/8 13:45:34 阅读更多 →
PyQt6中文教程终极指南:7天快速掌握Python桌面应用开发

PyQt6中文教程终极指南:7天快速掌握Python桌面应用开发

PyQt6中文教程终极指南:7天快速掌握Python桌面应用开发 【免费下载链接】PyQt-Chinese-tutorial PyQt6中文教程 项目地址: https://gitcode.com/gh_mirrors/py/PyQt-Chinese-tutorial 想要用Python创建专业的桌面应用程序吗?PyQt6中文教程为你提供…

2026/7/8 13:41:33 阅读更多 →
如何5分钟快速上手AcFunDown:免费开源A站视频下载器完整指南

如何5分钟快速上手AcFunDown:免费开源A站视频下载器完整指南

如何5分钟快速上手AcFunDown:免费开源A站视频下载器完整指南 【免费下载链接】AcFunDown 包含PC端UI界面的A站 视频下载器。支持收藏夹、UP主视频批量下载 😳仅供交流学习使用喔 项目地址: https://gitcode.com/gh_mirrors/ac/AcFunDown AcFunDow…

2026/7/8 13:39:33 阅读更多 →

日新闻

3大核心能力重塑《明日方舟》游戏体验:MAA自动化助手的革命性突破

3大核心能力重塑《明日方舟》游戏体验:MAA自动化助手的革命性突破

3大核心能力重塑《明日方舟》游戏体验:MAA自动化助手的革命性突破 【免费下载链接】MaaAssistantArknights 《明日方舟》小助手,全日常一键长草!| A one-click tool for the daily tasks of Arknights, supporting all clients. 项目地址: …

2026/7/8 0:00:48 阅读更多 →
MyBatis 批量操作深度优化——从 N+1 到批处理的全路径

MyBatis 批量操作深度优化——从 N+1 到批处理的全路径

MyBatis 批量操作深度优化——从 N1 到批处理的全路径 一、从"功能正确"到"性能可接受"——MyBatis 批量操作的三段式进化 MyBatis 在日常增删改查场景中几乎是无感的——实体映射直观、SQL 控制灵活。但当数据量从千级上升到十万级、百万级,许…

2026/7/8 0:00:48 阅读更多 →
工业负载控制方案:TPD2015FN与PIC18F45K22应用解析

工业负载控制方案:TPD2015FN与PIC18F45K22应用解析

1. 工业负载控制方案概述在工业自动化、电机驱动和照明控制等高需求场景中,可靠地控制电感和电阻负载是核心挑战之一。TPD2015FN作为东芝的8通道高端智能功率开关IC,配合PIC18F45K22微控制器,能够构建一套稳定、高效的负载控制系统。这套组合…

2026/7/8 0:02:48 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/7 14:24:45 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/7 12:34:47 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/7 15:59:06 阅读更多 →

月新闻