c++ 左值、右值、左值引用和右值引用

来源:互联网 发布:天下3 知乎 编辑:程序博客网 时间:2024/06/06 01:11

原文地址:http://blog.csdn.net/cjaymj/article/details/46311045
一、c++中的左值和右值
误区:左值位于等号左边,右值位于等号右边。
C++11中的定义:左值表达式表示的是一个对象的身份(在内存中的位置),而右值表达式表示的是对象的值(内容)。
左值和右值都是针对表达式而言的,左值是持久的,右值是短暂的:左值在表达式结束后仍然存在,右值在表达式结束后会被销毁。
区分左值和右值的方法:看能不能进行取地址操作,若能,则为左值,否则为右值。
注意:在需要右值的地方可以用左值来代替,但是不能把右值当成左值(也就是位置)使用。
例子:
若有如下定义:

[cpp] view plain copyint a = 10;  int b = 20;  int *p = &a;  vector<int> T;  T.push_back(1);  string s1 = “Hello”;  string s2 = "World";  const int &m =1;  

问:
a,b,a+b,a++,++a,p,*p,T[0],100,string(“Hello”),s1,s1+s2,m分别是左值还是右值?
答:
a,b是变量,变量可以看做只有运算对象而没有运算符的表达式,变量表达式都是左值。事实上,变量a,b均是长久的,在生命周期结束才被销毁,且我们能够对a,b进行取地址操作。故a,b均为左值。
a+b是临时变量,在该表达式结束时就被摧毁,且不能对其进行取地址操作,因此a+b为右值。
a++的作用机理是先将a的值拷贝到一个临时变量中,然后将这里临时变量加1,最终返回的是这个临时变量,因此a++为右值。
++a的作用机理是在原数据a上直接加1,最终返回的是原来的那个对象(只不过值加了1),因此++a为左值。
p表示的是指向a的指针,它也是长久的,并且我们能对其进行取地址操作,得到的是指向a的指针的地址。因此p为左值。
*p与a等价,也为左值。
T[0]返回容器T中第一个元素的引用,这是一个int型变量,是长久的,并且能对其进行取地址操作,因此T[0]为左值。
100是个常量,在使用过后就会销毁,并且不能对其进行取地址操作,因此100为右值。
string(“Hello”)与100类似,也是个常量,在使用过后就会销毁,并且不能对其进行取地址操作,因此string(“Hello”)为右值。
s1是string类型的变量,与a,b类似,是长久的,并且可以进行取地址操作。因此s1是左值。
s1+s2与a+b类似,是临时变量,在表达式结束就被摧毁,并且不能对其进行取地址操作。因此s1+s2是右值。
m是一个int类型的const左值引用,但它本身是一个变量表达式,因此m是左值。
二、左值引用和右值引用
左值引用符:&
右值引用符:&&
左值引用不能绑定到右值对象上,右值引用也不能绑定到左值对象上。
由于右值引用只能绑定到右值对象上,而右值对象又是短暂的、即将销毁的。也就是说右值引用有一个重要性质:只能绑定到即将销毁的对象上。
左值、右值引用的几个例子:

[cpp] view plain copyint i = 42;//如前所述,i是一个左值对象  int &r = i;//正确,左值引用绑定到左值对象i  int &&rr = i;//错误,右值引用绑定左值对象  int &r2 = i * 42;//错误,如前所述i*42是临时变量,是右值,而&r2是左值引用  int &&rr2 = i * 42;//正确,右值引用绑定右值对象 

注意:以上绑定规则有一个例外,如果左值引用是const类型的,则其可以绑定到右值对象上。(只有引用的const传递可以传递一个临时对象,因为临时对象都是const属性, 且是不可见的,他短时间存在一个局部域中,所以不能使用指针,只有引用的const传递能够捕捉到这个家伙

[cpp] view plain copyconst int &r3 = i * 42;//正确,我们可以将一个const的引用绑定到一个右值对象上  

对于一个左值,若想使用其右值引用,我们可以用move函数:

[cpp] view plain copyint &&rr3 = std::move(rr1);//正确,显式使用rr1的右值引用