范文健康探索娱乐情感热点
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

C中值类别和搬移,完美转发

  原来我也只知道C++中有左值和右值,通过今天的学习才知道,C++中不光有左值和右值。
  C++中有5种值类别, lvalue  ,  rvalue  ,  glvalue  ,  xvalue  ,  prvalue
  lvalue: 左值rvalue: 右值glvalue: generalized lvalue, 广义左值xvalue: expiring lvalue, 将亡值prvalue: prue rvalue, 纯右值
  看到这些不要害怕,我们一点点来。
  首先我们看下最熟悉的lvalue. lvalue
  左值是有标识符,可以取地址的表达式。 比如说: 变量,函数名,类的数据成员名这些都是变量名嘛 返回左值引用的表达式,如 ++x, x = 1, cout << " " 字符串字面值,如"hello world"
  值得注意的是, &  , reference 其实是左值引用,我们在调用函数时,传递的参数是左值,编译器就会匹配函数参数是左值的那一个函数。
  一个常量只能绑定到常左值引用,比如: const int a = 1; // int& b = a;  // 编译不过 const int& c = a; prvalue
  纯右值,是没有标识符,不可取地址的表达式,一般称之为 临时对象 非引用类型的表达式,如x++, x+1, make_shared(42)除字符串字面值之外的字面值,如 42, true
  在C++11之前,右值可以绑定到常左值引用的参数,但是不可以绑定到非 常左值引用。 如: // int& a = 1; // 编译不过 const int& a = 1;
  在C++11,引入了右值引用, &&  。这时我们就可以右值绑定到常右值引用,或者是非常右值引用了。 int&& a = 1; const int&& b = 1;
  引入一种额外的引用类型当然增加了语言的复杂性,但也带来了很多优化的可能性。由于 C++ 有重载,我们就可以根据不同的引用类型,来选择不同的重载函数,来完成不同的行为。 void f(int& a) {     cout << "int&" << endl; }  void f(int&& a) {     cout << "int&&" << endl; }  int main(int argc, char const* argv[]) {     int a = 1;     f(a);  // a 是一个左值,变量名     f(1);  // 数字1是一个右值     return 0; }
  输出: int& int&&
  那f内的a是左值还是右值呢? 都是左值,因为他们都是一个变量名。 类型右值引用的变量是一个左值 xvalue
  将亡值,也是一个右值
  标准库里有一个move函数, std::move  ,它的作用是把一个左值引用强制转换成一个右值引用,并不改变其内容。
  我们可以把move的返回值看成一个有名字的右值,为了跟无名的纯右值相区别,C++就把这种表达式叫做xvalue,xvalue也是不能取地址的。
  插播一个生命周期
  一个变量的生命周期在超出作用域时结束。如果一个变量代表一个对象,当然这个对象的生命周期也在那时结束。
  那临时对象(prvalue)呢?
  C++ 的规则是:一个临时对象会在包含这个临时对象的完整表达式估值完成后、按生成顺序的逆序被销毁,除非有生命周期延长发生。
  我们先看一个没有生命周期延长的基本情况: class shape { public:     virtual ~shape() {} };  class circle : public shape { public:     circle() { cout << "circle()" << endl; }     ~circle() { cout << "~circle()" << endl; } };  class triangle : public shape { public:     triangle() { cout << "triangle()" << endl; }     ~triangle() { cout << "~triangle()" << endl; } };  class result { public:     result() { cout << "result()" << endl; }     ~result() { cout << "~result()" << endl; } };  result process_shape(const shape& shape1,     const shape& shape2) {     return result(); }   process_shape(circle(), triangle());
  输出: triangle() circle() result() ~result() ~circle() ~triangle()
  运行之后你可以看到,result 临时对象最后生成,最先被析构。
  下面,我们用右值引用接收一下返回值, result&& r = process_shape(circle(), triangle());  triangle() circle() result() ~circle() ~triangle() ~result()
  你会发现,result的生成还在原来的位置,但是 析构 被延迟到了最后。
  我们在上一步的基础上 在 加上 move  ,  result&& r = move(process_shape(circle(), triangle()));  triangle() circle() result() ~result() ~circle() ~triangle()
  输出又变回了原来的样子。
  需要注意的是: 右值引用的声明周期延长只对 prvalue 有效,而对 xvalue无效。 如果由于某种原因,prvalue 在绑定到引用以前已经变成了 xvalue,那生命期就不会延长。不注意这点的话,代码就可能会产生隐秘的 bug。 移动
  C++出现移动的意义是什么? class A {   B b_;   C c_; };
  对于这样的代码,从实际内存布局的角度,很多语言如 Java 和 Python 会在 A 对象里放 B 和 C 的指针(虽然这些语言里本身没有指针的概念)。 而 C++ 则会直接把 B 和 C 对象放在 A 的内存空间里。这种行为既是优点也是缺点。说它是优点,是因为它保证了内存访问的局域性,而局域性在现代处理器架构上是绝对具有性能优势的。说它是缺点,是因为复制对象的开销大大增加:在 Java 类语言里复制的是指针,在 C++ 里是完整的对象。这就是为什么 C++ 需要移动语义这一优化,而 Java 类语言里则根本不需要这个概念。
  如何实现移动??
  通常需要下面几步: 类应该有拷贝构造和移动构造(除非你只打算支持移动,不支持拷贝————如 unique_ptr)可以参考我的文章 《C++手把手带你实现一个智能指针》 应该有 swap 成员函数,支持和另外一个对象快速交换成员 在你的对象的命名空间下,应当有一个全局的 swap 函数,调用成员函数 swap 来实现交换。支持这种用法会方便别人(包括你自己在将来)在其他对象里包含你的对象,并快速实现它们的 swap 函数。 实现通用的  operator=  上面各个函数如果不抛异常的话,应当标为 noexcept。
  我写了一个例子,可以参考一下,如果有问题还请指出来,我也是在学习过程中。 class A { public:     A(int a = 1) : m_a(a) {}     ~A() {} private:     int m_a; };  class B { public:     B() {}     B(A a) : m_a(a) {}     ~B() {}     void swap(B& rhs) noexcept {         using std::swap;         swap(m_a, rhs.m_a);     }     B(const B& b) noexcept {         m_a = b.m_a;     }     B(B&& rhs) noexcept {         swap(rhs);     }     B& operator=(const B& rhs) noexcept {         m_a = rhs.m_a;         return *this;     }     B& operator=(B&& rhs) noexcept {         swap(rhs);         return *this;     } private:     A m_a; };  void swap(B& a, B& b) {     a.swap(b); } int main() {     B b(100);     B b1 = b, b2;     b2 = b;      B b3(move(b));     B b4;     b4 = move(b2);      B b5(200), b6(1000);     swap(b5, b6); } 不要返回临时变量的引用
  刚学C++的时候,我就出现过把函数内的局部变量返回的情况,函数的返回值是一个引用。
  比如说: class A {  };  A& f() {  // 编译不过     return A(); }
  因为  f()   函数内返回的 A()  ,这个临时变量在f() 调用结束后就被销毁了,返回一个指向本地对象的引用属于未定义行为。
  所以正确的写法应该是把 &   去掉, A& f()  改为 A f()  , C++11之前,编译器会自动把这个临时对象拷贝一份作为函数的返回值传递回来。
  除非编译器发现可以做返回值优化(named return value optimization,或 NRVO),能把对象直接构造到调用者的栈上。
  从 C++11 开始,返回值优化仍可以发生,但在没有返回值优化的情况下,编译器将试图把本地对象移动出去,而不是拷贝出去。
  返回值优化不需要我们去改 move  , 写了 move  反而弄巧成拙,会取消返回值优化。 class A { public:     A() { cout << "A" << endl; }     A(const A& a) { cout << "A&" << endl; }     A(A&& a) { cout << "A&&" << endl; } };  A f() {     return A(); }  A f2() {     return move(A()); }  int main() {     f();     cout << "---------------" << endl;     f2();     return 0; }
  输出: A --------------- A A&&
  f()  ,编译器对返回值进行了优化,对象直接被构造到调用者的栈上(没有被拷贝和搬移),
  f2()  ,move 破坏了返回值优化,相对于f(),对象还被多搬移了一次,这个搬移属实没必要。 完美转发 void f2(int& a) {     cout << "f2 int&" << endl; }  void f2(int&& a) {     cout << "f2 int&&" << endl; }  void f(int& a) {     cout << "int&" << endl; }  void f(int&& a) {     cout << "int&&" << endl;     f2(a); }  int main() {     int a = 1;     f(move(a));     return 0; }
  输出: int&& f2 int&
  我们之前有说到, void f(int&& a)  ,a在f的函数体内又变成了左值,因为它是一个变量,那么我们怎么才能保证a原来是右值,进入函数体以后还是一个右值呢?
  这就用到了完美转发, std::forward()  , 我们把上述代码的 void f(int&& a) {     cout << "int&&" << endl;     f2(a); }
  改成 void f(int&& a) {     cout << "int&&" << endl;     f2(forward(a)); }
  输出: int&& f2 int&&
  完美转发, forward  是一个函数模板,我们则需要把类型传递进去,这个例子传递的是一个int类型。
  好了,值类别和搬移先说到这里了,如果文章有错误的地方还请给我指出来,大家一起进步嘛。
  如果觉得对你有帮助的话请@程序员杨小哥 点个赞,谢谢!

真正的无人驾驶还有多远?本文来自微信公众号笔记侠(IDNotesman),作者吴甘沙,责任编辑智勇,值班编辑金木研。内容来源湛庐AI3。0全智能场景新书发布会。分享嘉宾吴甘沙,驭势科技联合创始人董事长兼C华为的接班人来了?OPPO或将自研芯片,代号马里亚纳今年的国内手机市场,正在发生着翻天覆地的变化。由于华为受到的芯片制裁,其市场份额正在大幅度缩水,曾经的国产手机一哥现如今在高端旗舰市场当中已经站不稳脚跟,vivo小米OPPO等手机韩国手机要卷土重来韩国的LG电子计划将最后一批手机彩虹(Rainbow),以每部19万韩元(约1080元人民币)的价格向自己的员工内部出售后,并于7月31日彻底退出移动电子产品的舞台。从此以后,所谓反击即将开始!荣耀50再次被确认或搭载骁龙778G100W自从荣耀手机独立出来之后,有很多用户期待浴火重生,也有用户期待荣耀手机填补华为手机的空缺。但从此前的发展情况来看,荣耀手机的力度并不强,发布的荣耀V40系列真的有一种不痛不痒的感觉全新奥迪A8L插电混动版全时四驱空气悬架,车内空气更迷人作为豪华汽车ABB的三大剑客之一,奥迪在我国市场上的占有率越来越高,拥有百年历史的奥迪品牌,一直以来就是以豪华科技而著称。这次这个历经百年沉淀的品牌又拿出了杀手锏,全面进军新能源领鸿蒙OS2。0即将开源,是自研还是套壳,460万关键代码揭晓答案自从华为宣布自家的鸿蒙OS之后,外界的各种质疑声就一直不断。就像华为最开始宣布研发麒麟芯片的时候,基本上没有什么人认为华为会成功,但是经过这么多年的努力,华为的麒麟芯片已经完全不输四款骁龙870机型盘点,性能够用三年,价格也仅两千左右今年安卓机主流的是两款芯片是骁龙888和骁龙870,前者基本都是各大高端旗舰的标配,价格一般都在三四千以上,超出了大部分消费者的预算。而后者虽然略微逊色一点,但性能还在去年的骁龙8华为每年有大量员工受贿被处理华为每年有大量员工受贿被处理5月17日,华为轮值董事长徐直军表示,我们的员工的受贿类行为每年要处理的量都是很大的。有一些人到了派出所或者到了监狱,他们的太太们都会用各种方式求情。我苹果推出无损音乐,玄学还是革命?流媒体音乐可能正在进入高保真音频的新时代。在AppleMusic宣布新动作后,科技媒体TheVerge这样惊呼1。5月17日,苹果宣布,AppleMusic将在2021年6月支持无华为每年都有员工因受贿入狱,任正非谈内部腐败我不妥协华为轮值董事长徐直军在5月17日开幕的华为中国生态大会2021上表示,我们每年要处理比较多员工受贿的情况,有一些人进了派出所,或者监狱。其实,华为公司从初期到现在,一直面临着内部的火币网交易所疑在国内开展实质业务投资者权益或因霸王条款难以保障中国科技投资刘逸伦北京寻真律师事务所王德怡认为虚拟货币创造暴富的神话,但这个领域更多的是风险和陷阱。今年以来,在警方公布的多起电信诈骗和洗钱犯罪案件中,虚拟货币交易所频被涉及,其中
618年中大促进行中,这篇文章教你买手机如何避雷,选择最值机型618年中大促现在正在火热进行中,高考也如约而至,相信很多小伙伴们都打算在这段期间,给自己买很多之前想买的东西,比如说手机,毕竟现在这个时期,手机厂商都会进行各种优惠,或者是赠送各拼多多变香之后,盯上它的可不止是一家平台在几年前,拼多多这个电商平台可谓是过街老鼠,人人喊打。相信大家都知道原因,毕竟它里面假货多的出奇,比如买的雪碧,变成了雷碧奥利奥变成了粤利粤清扬洗发水变成了清场等等。虽然它的价格便不会吧不会吧,苹果居然开始造车了?对此你怎么看如果,苹果造车你们会买吗?是的,不要怀疑,苹果已经开始在汽车领域上研发了。据了解,苹果自从2014年以来就已经开始进军汽车领域了,但是它的进展并不顺利,而目前苹果公司现在的目标是在即将2021年,回顾今年手机厂商的主打亮点,这些你都知道吗?再过几天,2020年就要结束了,正式进入2021年。回顾今年,似乎谁都不好过,各种困难,其中手机圈也是如此。一边是疫情的原因,一边又是5G时代,因此很多手机厂商都想在这一年里面改变为何手机容量已经大了这么多,我们却还是不够用?如今,手机的存储空间越来越大,从刚开始的2GB,到现如今的1个T容量,甚至各大手机厂商都已经不再推出100GB容量一下的机型,现在起步就是128GB的手机容量,甚至运行内存都有16买手机不知道买什么型号?看完这篇文章就知道了纵观目前手机市场上,可以说是有成千上万的机型,每个价位段的机型主打的性能拍照外观续航等各方面都不一样。作为消费者的我们,很难在其中选择一款值得入手的机型,又或者说,很容易被线下店里大数据时代,究竟带给我们的是便利还是麻烦?面对现如今的大数据时代,对于我们消费者来说,究竟是件好事还是坏事?相信屏幕前的小伙伴们都有遇到过,不管咋哪个第三方应用软件上,都能看到自己前一阵子提起过的东西,明明自己并没有拿手机一年一度的换机潮来临,收好这个攻略才不会被店员忽悠距离过年回家探亲的日子越来越近,不少小伙伴们已经开始准备回家风光的装备了,比如说一件看起来上档次的衣服和装饰。而要说起装饰,相信有很多小伙伴们都会在这个时候,准备入手一部新的手机,为什么不喜欢发朋友圈了?(终篇)年纪越大,越喜欢隐藏自己年轻的时候,吃了一家好吃的火锅,剪了一个帅气的发型,追到了心仪已久的女孩都要昭告天下什么都想说,所有的悲伤和欢乐都想和朋友分享。后来慢慢走出社会,年纪越来越闲事勿多管人生有四大多管的闲事扶烂泥,雕朽木,翻咸鱼,烫死猪。每个人的生活轨迹不同,不必把你的价值观,强加给别人。不是分内之事,不管,他人的私事非邀,不管,不危害社会公利的事,少管。分清自己好心态,过好生活看过这样一段话说生活有时候,就像你口渴时喝水,你手上拿着白开水,却羡慕别人手里的饮料,其实饮料未必比白开水解渴,别人拥有的,不一定比你好,总是盯着别人,你就失去了自己。不管是谁,总