; 比方: int i=0;// i是左值, 0是右值 2、左值引用: c++98中的引用很常见了,就是给变量取了个别名,在c++11中,因为增加了右值引用(rvalue reference)的概念,所以 1是右值,不能够使用左值引用 3、右值引用,c++11中的右值引用使用的符号是&&,如: int&& a = 1; //实质上就是将不具名(匿名)变量取了个别名 int b = 1; int && c ; //getTemp()的返回值是右值(临时变量) 总结一下,其中T是一个具体类型: 左值引用, 使用 T&, 只能绑定左值; 右值引用, 使用 T&&, 只能绑定右值; 常量左值, 使用 const T&, 既可以绑定左值又可以绑定右值; 已命名的右值引用,编译器会认为是个左值; 编译器有返回值优化,但不要过于依赖; Q:下面涉及到一个问题:x的类型是右值引用,指向一个右值,但x本身是左值还是右值呢 参考:[c++11]我理解的右值引用、移动语义和完美转发 https://www.jianshu.com/p/d19fc8447eaa C++ 11 左值,右值,左值引用,右值引用,std::move
= lv1 + lv1; // 合法, 常量左值引用能够延长临时变量的生命周期 // lv2 += "Test"; // 非法, 引用的右值无法被修改 std::cout << "lv2 std::string&& rv2 = lv1 + lv2; // 合法, 右值引用延长临时对象的生命周期 rv2 += "string"; // 合法, 非常量引用能够修改临时变量 虽然引用了一个右值,但由于它是一个引用,所以 rv2 依然是一个左值。 const int &b =2; # 常量左值引用绑定到右值,编程通过 右值值引用通常不能绑定到任何的左值,要想绑定一个左值到右值引用,通常需要std::move()将左值强制转换为右值,例如: int 下面是按照判决的优先级列出的3条规则: 1、常量值只能绑定到常量引用上,不能绑定到非常量引用上。 2、左值优先绑定到左值引用上,右值优先绑定到右值引用上。
int main() { // 以下的p、b、c、*p都是左值 int* p = new int(0); int b = 1; const int c = 2; // 以下几个是对上面左值的左值引用 右值引用就是对右值的引用,给右值取别名。 (不是右值引用的重点使用场景) 先来看一段代码: int main() { double x = 1.1, y = 2.2; int&& rr1 = 10; const double&& rr2 rr2 = 5.5; // 报错 return 0; } 右值是不能取地址且不可修改的,但是给右值取别名后,会导致 右值被存储到特定位置,且可以取到该位置的地址 即变成左值了,也就是说例如:不能取字面量 模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值。
我们可以在自己的类中实现移动语义,避免深拷贝,充分利用右值引用和std::move的语言特性。 移动语义目的就是用浅拷贝代替深拷贝,右值引用跟深拷贝放到同一场景才是有意义的。 实现移动语义 在没有右值引用之前,一个简单的数组类通常实现如下,有构造函数、拷贝构造函数、赋值运算符重载、析构函数等。 当然函数参数可以改成非const:Array(Array& temp_array, bool move){…},这样也有问题,由于左值引用不能接右值,Array a = Array(Array(), true 可以发现左值引用真是用的很不爽,右值引用的出现解决了这个问题,在STL的很多容器中,都实现了以右值引用为参数的移动构造函数和移动赋值重载函数,或者其他函数,最常见的如std::vector的push_back 参数为左值引用意味着拷贝,为右值引用意味着移动。
mc) { std::cout << "int func(MClass&& mc)|&mc:" << &mc << std::endl; return 0; } int func2(
右值引用是C++11标准引入的一个技术。 与左值引用类似,右值引用的是右值,包括常量、临时值等不可作为左值的值,使用&&表示右值引用,如:type &&t = value1+value2;,在标准库的头文件<uility>有std::move( 如果是const 左值引用类型,则同样可以接收右值。 zmh 使用右值引用时,要防止右值销毁而使获取的资源无效。 以上是对右值引用的简单介绍,欢迎大家一起交流讨论。
通俗来讲,凡是可以出现在赋值运算符左边的表达式都是左值。与左值相对的就是右值(Rvalue),只能出现在赋值运算右边的表达式都是右值,所以,左值一定可以作为右值,右值一定不能作为左值。 (2)常变量虽然可以寻址,但是由于只读的限制,也不能作为左值。 (3)如果表达式的运算结果是一个由文字常量生成的临时无名对象,则表达式不能作为左值,如下面的例子。 在statement1中,由于表达式i+=1的运算结果是对i的引用,所以它也可以作为左值。而在statement2中,函数调用func()的返回结果是对局部变量a的引用,所以该表达式也可以作为左值。 2.建立引用的条件 由于引用变量中实际上存放的是被引用对象的地址,所以,左值一定可以建立非常引用。 ---- 参考文献 [1]C++高级进阶教程.陈刚.武汉大学出版社.1.9左值的概念 [2]C专家编程(中文版).第4章
C++11 引入了右值引用(Rvalue References)的概念,它是一种新的引用类型,与传统的左值引用(Lvalue References)相对应。右值引用主要用于支持移动语义和完美转发。 例如,字面常量、函数返回的右值、显式使用 std::move() 转换后的对象等都是右值。 右值引用是用来绑定和延长临时对象(右值)生命周期的引用类型。 通过使用双 ampersand(&&)来声明右值引用。 例如: int&& rv = 42; // 右值引用绑定到右值(字面常量) 右值引用的特点和用途包括: 移动语义(Move Semantics):右值引用在移动语义中发挥了重要作用。 通过使用模板和右值引用参数,可以在函数内部将参数作为右值或左值传递给其他函数,达到完美转发的效果。 临时对象的延长生命周期:使用右值引用可以将临时对象的生命周期延长,使其可以在更长时间内使用。
这是EasyC++系列的第38篇,来聊聊右值引用。 右值引用 左值和右值 在我们之前的文章当中,介绍的都是左值引用。 C++11在左值引用的基础上推出了右值引用,由于是新特性,加上使用的频率也不是很高,有一定的学习成本。 我们先把引用这个概念抛开,先来看看什么是左值什么是右值。 左值引用和右值引用 明白了左值、右值的概念再来看看左值引用、右值引用就简单了。左值引用顾名思义就是能够指向左值不能指向右值的引用。 比如vector当中的push_back: void push_back (const value_type& val); 右值引用和左值引用的概念类似,也就是能够指向右值但不能指向左值的引用。 ,除了左值引用、右值引用之外还有非常多的细节。
右值引用和移动语义 传统的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,所以从现在开始我们之前学习的引用就叫做左值引用。无论左值引用还是右值引用,都是给对象取别名。 // 以下的p、b、c、*p都是左值 int* p = new int(0); int b = 1; const int c = 2; // 以下几个是对上面左值的左值引用 int*& rp ) << endl; // 以下几个都是对右值的右值引用 int&& rr1 = 10; double&& rr2 = x + y; double&& rr3 = fmin(x, y); 右值引用就是对右值的引用,给右值取别名 左值右值 左值引用可以引用右值吗?右值引用可以引用右值吗? // 有条件的支持 // 左值引用可以引用右值吗? 但是const左值引用既可引用左值,也可引用右值。 右值引用总结 右值引用只能引用右值,不能引用左值 右值引用可以引用move之后的左值 注意 rr1和rr2可以取地址了,它们是左值了。
y; //给表达式x+y取别名为r6 左值引用和右值引用比较 左值引用 左值引用只能引用左值,不能引用右值 但是const左值引用既可以引用左值,也可以引用右值 /左值引用引用右值 double x = 2.2; double y = 3.3; const int& r2 = 10; const double& r3 = x + y;//这里x和y都是左值,但是x+y表达式返回的结果5是一个临时变量是右值 右值引用 右值引用只能引用右值,不能引用左值. int main() { string s1("hello world"); // 这里s1是左值,调用的是拷贝构造 string s2(s1); // 这里我们把s1 , 就需要用我们下面学习的完美转发 } 2. std::forward<T> 作用:根据 T 的原始类型(左值或右值),将参数有条件地转换回原始类型。
代码编译运行环境:VS2017+Win32+Debug 文章目录 1.左值的定义 2.建立引用的条件 3.常引用的特殊性质 参考文献 1.左值的定义 左值(Lvalue)是 C++ 中的一个基本概念, 通俗来讲,凡是可以出现在赋值运算符左边的表达式都是左值。与左值相对的就是右值(Rvalue),只能出现在赋值运算右边的表达式都是右值,所以,左值一定可以作为右值,右值一定不能作为左值。 (2)常变量虽然可以寻址,但是由于只读的限制,也不能作为左值。 (3)如果表达式的运算结果是一个由文字常量生成的临时无名对象,则表达式不能作为左值,如下面的例子。 在statement1中,由于表达式i+=1的运算结果是对i的引用,所以它也可以作为左值。而在statement2中,函数调用func()的返回结果是对局部变量a的引用,所以该表达式也可以作为左值。 2.建立引用的条件 由于引用变量中实际上存放的是被引用对象的地址,所以,左值一定可以建立非常引用。
右值引用就是对右值的引用,给右值取别名。 右值引用是用两个&&表示,左值引用是用一个&表示。 第一个是在右值引用后,竟然可以对右值引用重新赋值! 第二个是右值是不能取地址的,但是给右值取别名后,这个别名可以被取地址! 这是右值引用的特性! ②如果加了const修饰的左值引用,可以引用左值和右值。因为有了const修饰,要么是权限的平移,要么是权限的缩小。 ③右值引用只能引用右值,不能引用左值。 ④右值可以引用move后的左值。 int a = 10; int& ra1 = a; // ra1为a的别名,左值引用引用左值,a为左值 //int& ra2 = 10; // 编译失败,因为10是右值,左值引用不能引用右值 && r2 = a; //a是左值,&&是右值引用,右值引用不能引用左值 // 右值引用可以引用move以后的左值 int&& r3 = std::move(a); return 0; } 右值引用的作用和意义
右值引用在编码和性能上确实带来了不少的便利。 2.在windows(vs2015)下情况就有点特殊:只在第一个return使用了RVO,后面的return并没有 PS:本人以前研究底层的汇编的原因,所以比较喜欢看原理,从汇编角度看RVO的实现原理 其实是编译器偷偷的把外部返回值接收者的变量的地址做为一个隐藏参数传进了函数,这就为RVO提供了充分的可能条件。 理解指针和内存的话,其实什么魔法都逃不了这2个概念。 以前的参数引用 也是如此(传了变量的指针,原理不变,只是编码上换了新样) 类函数访问类成员也是如此(对象作为ECX传进函数)
c = 2; // 以下几个都是常见的右值 10; x + y; fmin(x, y); } 2.左值引用和右值引用的概念 那么我们就可以很容易地知道: 左值引用:给左值取别名 右值引用:给右值取别名 int a = 10; int& ra1 = a; // ra1为a的别名 //int& ra2 = 10; // 编译失败,因为10是右值 // const左值引用既可引用左值,也可引用右值 int&& r1 = 10; int a = 10; //message : 无法将左值绑定到右值引用 int&& r2 = a; //右值引用可以引用move以后的左值 int ---- 2.左值引用和右值引用引出 左值引用的意义在于: 1.函数传参:实参传给形参时,可以减少拷贝。 2.函数传返回值时,只要是出了作用域还存在的对象,那么就可以减少拷贝。 (直接资源交换) 总结: ---- 2.对于插入右值数据时,也可以减少拷贝 只有左值引用时的插入接口: STL容器插入接口函数也增加了右值引用版本: 会直接进行资源交换,将将亡值和新创建的节点中的数据进行资源交换
2.右值引用 2.1右值引用简介 为了支持移动操作,C++11引入了一种新的引用类型——右值引用(rvalue reference)。所谓的右值引用指的是必须绑定到右值的引用。使用&&来获取右值引用。 下面是左值引用与右值引用示例: int i=42; int& r=i; //正确,左值引用 int&& rr=i; //错误,不能将右值引用绑定到一个左值上 int& r2=i*42; //错误 ,i*42是一个右值 const int& r3=i*42; //正确:可以将一个const的引用绑定到一个右值上 int&& rr2=i*42; //正确:将rr2绑定到乘法结果上 从上面可以看到左值与右值的区别有 示例如下: int&& rr1=42; int&& rr2=rr1; //error,表达式rr1是左值 int&& rr2=std::move(rr1); //ok 上面的代码说明了右值引用也是左值 简单总结为: (1)所有右值引用折叠到右值引用上仍然是一个右值引用; (2)所有的其他引用类型之间的折叠都将变成左值引用。
为理解这两个概念需要先了解以下内容: 左值,右值 拷贝构造函数和复制构造函数 左值和右值 一般来说,左值代表某处内存区域,相对的,右值只代表值 #include <iostream> #include 右值 ** int tmp = 10; ** tmp 是一个左值,左值一般是变量,可以被引用,10是一个右值,不可以被引用. ** 一般来说,左值代表某处内存区域,相对的,右值只代表值 */ void 和 std::move ** 右值引用是用来支持转移语义的。 回到原题 为什么需要右值引用? 右值引用其实就为给匿名(天生匿名或者通过 std::move 将名字失效,这样的对象即将被析构)对象重新起名字。 我们一直所说的将亡值其实就是所谓的右值,我们可以利用右值引用将将亡值利用起来,减少不必要的构造和析构。
可以对它赋值 性质2: 定义时const修饰符后的左值 , 不可以对它赋值 ,但是 可以对它取地址 左值引用: 左值引用就是给左值的引用,给左值取别名 int a = 0; int& r1 = a int a = 0; int& r1 = a; } 【2】右值&右值引用 右值: 右值也是一个表示数据的表达式 如: 字面常量、表达式返回值,函数返回值(这个不能是左值引用返回)等等、 出现位置: 右值可以出现在赋值符号的右边 int&& rr1 = 10; double&& rr2 = x + y; double&& rr3 = fmin(x, y); } 二.左值引用右值&右值引用左值の规则 基本规则: 引用是 取别名 左值引用:给左值取别名————————(1)正常左值引用(2)带const的左值引用 右值引用:给右值取别名 move( )可以让里面的值具有 右值性质 左值引用右值&右值引用左值 // const左值引用可以 const int& r2 = 10; const double& r3 = x + y; // 右值引用:给右值取别名 int&& r5 = 10; double
("%p %p\n", ptr1, ptr2); return 0; } 什么是右值,什么是右值返回? func1返回的是右值,因为返回的是x的拷贝,拷贝的临时变量就是右值;而func2返回的是别名,因此就是左值 小总结: 语法上: 能否取地址是区分左值和右值的关键 引用都是别名,不开空间,左值引用是给左值取别名 int& r2 = 10; // 右值引用能否给左值取别名 不能 // 但是右值引用可以给move以后的左值可以 int x = 0; int&& rr1 = move(x); return C++11对右值概念的解释,细分便于理解 1、纯右值(内置类型的右值)如:10 / a + b 2、将亡值(自定义类型的右值)如:匿名对象、传值返回函数 C++提供右值引用,本质是为了参数匹配时,区分左值和右值 假如有string s2(tmp),系统识别到了这个tmp是右值将亡值,就会调用移动拷贝,直接将tmp的资源和s2进行交换! 认为如果是将亡值进行深拷贝就是极度的浪费!
右值引用 右值引用也就是对右值取别名,用符号&&来声明,比如: int x = 2, y = 1; //下面是常见的右值 10; "xxxxxx"; x + y; fmax(x, y); 右值引用本身是左值! 右值引用本身是左值! 也就是说上面代码中的 a,b,c,d均是左值!!! 原因很简单,如果右值引用本身还是右值,那么右值引用将毫无意义,无法修改,进行后续操作。 右值引用的诞生 c++11更新后为了弥补左值引用的不足,创造出了右值引用,完全彻底避免了不必要的拷贝,没错就是右值引用返回。 继续看到上面的代码,引入一个新名词:将亡值 也就是str2,顾名思义,将亡值也就是即将消亡的值,正如str2出了函数体后直接释放,那么右值引用又是如何解决这个问题的呢? str1); return move(str2); } private: string _str; }; 右值引用返回+移动构造,通过上述代码不难看出,移动构造其实就是将即将释放的将亡值str2