【C++11】初识——右值引用和移动语义,可变参数模板

发布时间:2026/10/24 5:29:15
【C++11】初识——右值引用和移动语义,可变参数模板 个人主页ꪔ小林Y✨个人专栏《C》《C语言》《数据结构入门——从原理到实战》,《拾光Linux》《网络安全》代码信条每一行代码都是成长的脚印每一次调试成功都是对坚持的回应目录【C11】11.右值引用和移动语义1.1左值和右值1.2.左值引用和右值引用1.3引用延长生命周期1.4移动构造和移动赋值1.5引用折叠1.6完美转发2.可变参数模板2.1概念理解2.2折叠表达式c172.3emplace接口2.4模拟实现list的emplace_back【C11】11.右值引用和移动语义C11之前学的引用是左值引用C11新增了右值引用语法特性无论是左值引用还是右值引用都是给对象取别名。1.1左值和右值左值lvalue一个表示数据的表达式存储在内存中可以获取它的地址左值可以出现在赋值符号的左边也可以在右边。定义时const修饰符后的左值不能给它赋值但可以取它的地址。如存储在内存中有明确存储地址可以取地址的对象。intmain(){//左值可以取地址//以下的p,b,c,*p,s,s[0]就是常见的左值int*pnewint(0);intb1;constintcb;*p10;strings(11111);s[0]x;}右值rvalue是一个数据的表达式要么是字面值常量要么是表达式求值过程中创建的临时对象等右值可以出现在赋值符号的右边但是不能出现在赋值符号的左边右值不能取地址。如临时变量字面量常量存储于寄存器中的变量等。//右值不能取地址doublex1.1,y2.2;//以下10xyfmin(x,y),string(11111)都是常见的右值10;xy;fmin(x,y);string(11111);1.2.左值引用和右值引用左值引用就是给左值取别名例int r1x;,右值引用同样的道理例int rr1y;左值引用不能直接引用右值但是const左值可以引用右值,例const int rx110;右值引用不能直接引用左值但是右值引用可以引用move(左值),例:int rrx1move(b);注意变量表达式都是左值属性也就意味着一个右值被右值引用绑定后右值引用变量变量表达式的属性是左值。左值引用解决的效率问题传左值引用传参减少拷贝传左值引用返回减少拷贝部分场景左值和右值的参数匹配C11以后分别重载左值引用、const左值引用、右值引用作为形参的f函数那么实参是左值会匹配f(左值引用)实参是const左值会匹配f(const 左值引用)实参是右值会匹配f(右值引用)。1.3引用延长生命周期右值引用可用于为临时对象延长生命周期const 的左值引用也能延长临时对象生存期但这些对象无法被修改。1.4移动构造和移动赋值移动构造函数是⼀种构造函数类似拷贝构造函数移动构造函数要求第⼀个参数是该类类型的引用但是不同的是要求这个参数是右值引用如果还有其他参数额外的参数必须有缺省值。移动赋值是⼀个赋值运算符的重载他跟拷贝赋值构成函数重载类似拷贝赋值函数移动赋值函数要求第⼀个参数是该类类型的引用但是不同的是要求这个参数是右值引用。对于像string/vector这样的深拷贝的类或者包含深拷贝的成员变量的类移动构造和移动赋值才有意义因为移动构造和移动赋值的第一个参数都是右值引用的类他的本质是要“窃取”引用的右值对象的资源而不是像拷贝构造和拷贝赋值那样去拷贝资源从提高效率。1.5引用折叠通过模板或者typedef中的类型操作可以构成引用的引用。规则右值引用的右值引用折叠成右值引用左右其他组合均折叠成左值引用。intmain(){//int ri;这样直接引用是有问题的会报错typedefintlref;typedefintrref;intn0;//引用折叠lrefr1n;//r1的类型是intlrefr2n;//r2的类型是intrrefr3n;//r3的类型是intrrefr41;//r4的类型是int}//由于引用折叠限定f1实例化以后总是一个左值引用templateclassTvoidf1(Tx){}//例//1.没有折叠-实例化为void f1(int x)f1int(n);f1int(0);//传右值会报错//2.折叠-实例化为void f1(int x)f1int(n);f1int(0);//传右值会报错//3.折叠-实例化为void f1(int x)f1int(n);f1int(0);//传右值会报错//4.折叠-实例化为void f1(const int x)f1constint(n);f1constint(0);//5.折叠-实例化为void f1(const int x)f1constint(n);f1constint(0);//由于引用折叠限定f2实例化以后可以是左值引用也可以是右值引用templateclassTvoidf2(Tx){}//例//1.没有折叠-实例化为void f2(int x)f2int(n);//报错f2int(0);//2.折叠-实例化为void f2(int x)f2int(n);f2int(0);//报错//3.折叠-实例化为void f2(int x)f2int(n);//报错f2int(0);由此引出了万能引用传左值实例化出左值引用的Function函数传右值实例化右值引用的Function函数templateclassTvoidFunction(Tt){inta0;T x1;coutaendl;coutxendlendl;}1.6完美转发变量表达式都是左值属性也就意味着一个右值被右值引用绑定后右值引用变量表达式的属性是左值我们想要保持对象的属性就需要使用完美转发实现。templateclass TTforward(typename remove_referenceT::typearg);templateclass TTforward(typename remove_referenceT::typearg)templateclassXvoidpush_back(Xx){//完美转发是为了//保持x的属性往下传递//实例化push_back(Lin::string x),保持右值属性往下传递//实例化push_back(Lin::string x),保持左值属性往下传递insert(end(),forwardX(x));}2.可变参数模板2.1概念理解C11支持可变数量参数的函数模板和类模板可变数目的参数被称为参数包存在两种参数包模板参数包templateclass… Args表示零或多个模板参数函数参数包void Func(Args… args)args为零或多个参数表示零或多个函数参数。通过…触发参数包展开的过程叫包扩展template class ...Args void Func(Args... args) {}//传值形参template class ...Args void Func(Args... args) {}//传左值引用形参template class ...Args void Func(Args... args) {}//传万能引用形参用省略号指出⼀个模板参数或函数参数的⼀个包。函数参数包可以用左值引用或右值引用表示每个参数实例化时遵循引用折叠规则。2.2折叠表达式c17templateclass...ArgsvoidPrint(Args...args){((coutargs ),...);cout\n;}intmain(){Print();Print(1);Print(1,string(xxxx));Print(1,string(xxxxx),2.2);return0;}2.3emplace接口empalce系列的接口均为模板可变参数功能上兼容push和insert系列但是empalce还支持新玩法假设容器为containerempalce还支持直接插入构造T对象的参数这样有些场景会更高效⼀些可以直接在容器空间上构造T对象。templateclass... Argsvoid emplace_back(Args... args);templateclass... Argsiterator emplace(const_iterator position, Args... args);empalce_back和push_back对比push_back需先构造临时对象再通过拷贝 / 移动构造到容器中emplace_back接收构造对象的参数包直接在容器节点中构造对象减少一次拷贝 / 移动。下面场景详细看一下区别相同 例构造pair拷贝/移动构造pair到list节点中data上不同例直接把构造pair参数包往下传直接用pair参数包构造pairpush_back做不到lt1.emplace_back(苹果,1);//lt1.push_back(苹果,1);错误要传pair或者{}隐式转换pair的值 lt1.push_back({苹果,1});2.4模拟实现list的emplace_back#pragmaoncenamespaceLin{templateclassTstructlist_node{list_nodeT*_next;list_nodeT*_prev;T _data;list_node(Tdata):_next(nullptr),_prev(nullptr),_data(move(data))// 移动构造或拷贝构造{}templateclass...Argslist_node(Args...args):_next(nullptr),_prev(nullptr),_data(std::forwardArgs(args)...){}};templateclassT,classRef,classPtrstructlist_iterator{usingSelflist_iteratorT,Ref,Ptr;usingNodelist_nodeT;Node*_node;list_iterator(Node*node):_node(node){}// *it 1Refoperator*(){// ……}Ptroperator-(){// ……}// itSelfoperator(){// ……}Selfoperator(int){// ……}// --itSelfoperator--(){// ……}Selfoperator--(int){// ……}booloperator!(constSelfs)const{// ……}booloperator(constSelfs)const{// ……}};templateclassTclasslist{usingNodelist_nodeT;public:usingiteratorlist_iteratorT,T,T*;usingconst_iteratorlist_iteratorT,constT,constT*;// using iterator list_iteratorT;// using const_iterator list_const_iteratorT;iteratorbegin(){// ……}iteratorend(){// ……}const_iteratorbegin()const{// ……}const_iteratorend()const{// ……}voidempty_init(){// ……}list(){// ……}list(initializer_listTil){// ……}templateclassInputIteratorlist(InputIterator first,InputIterator last){// ……}list(size_t n,T valT()){// ……}list(intn,T valT()){// ……}~list(){// ……}// 传统写法// lt2(lt1)list(constlistTlt){// ……}// lt1 lt3;listToperator(constlistTlt){// ……}voidswap(listTlt){// ……}voidclear(){// ……}voidpush_back(constTx){// ……}// 不是万能引用// 因为T是list的参数listLotso::string实例化时T就确定了。voidpush_back(Tx){// ……}voidpush_front(constTx){// ……}voidpop_back(){// ……}voidpop_front(){// ……}voidinsert(iterator pos,constTx){// ……}voidinsert(iterator pos,Tx){// ……}iteratorerase(iterator pos){// ……}size_tsize()const{// ……}templateclass...Argsvoidemplace_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)); } */templateclass...Argsiteratorinsert(iterator pos,Args...args){Node*curpos._node;Node*newnodenewNode(std::forwardArgs(args)...);Node*prevcur-_prev;// prev newnode curprev-_nextnewnode;newnode-_prevprev;newnode-_nextcur;cur-_prevnewnode;returniterator(newnode);}private:Node*_head;size_t _size0;};}到这儿本期【C11】初识——右值引用和移动语义可变参数模板的内容就结束了。如果文中有表述不准的地方或是你有更清晰的理解思路强烈欢迎在评论区留言交流——技术路上多碰撞才能更快进步觉得内容对你有帮助的话别忘了点赞❤️➕收藏方便后续回顾复习想跟着一起系统学习数据结构的朋友也可以点击关注下一期我们会聚焦更进一步的学习。不见不散✌️

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询