上篇文章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; }; }本章完。