我们通过&&而不是&来获得右值引用。右值引用有一个重要的性质——只能绑定到一个将要销毁的对象。因此,我们可以自由地将一个右值引用的资源“移动到”另一个对象。
一个右值引用也是引用,不过是某个对象的名字而已。
先看网上的blog的解释
http://www.cnblogs.com/hujian/archive/2012/02/13/2348621.html
左值和右值都是针对于表达式而言的,左值是指表达式结束后仍然存在的持久对象,右值是指表达式结束后就不在存在的临时对象。一个区分左值和右值的便捷方法:看能不能对表达式取地址,能为左值,不能为右值
int a = 10;
int b = 20;
int *pFlag = &a;
vector<int> vctTemp;
vctTemp.push_back(1);
string str1 = "hello ";
string str2 = "world";
const int &m = 1;
a和b都是持久对象(可以对其取地址),是左值。
a+b是临时对象(不能对其取地址),是右值。
a++先取出持久对象的一个拷贝,再使持久对象a的值加1,最后返回那份拷贝,而那份拷贝是临时对象(不能对其取地址),是右值。
++a使持久对象加1,并返回那个持久对象本身(可以对其取地址),是左值。
pFlag和*pFlag都是持久对象(可以对其取地址),是左值。
vectTmp[0]调用了重载的[]运算符,而[]运算符返回的是一个int&,为持久对象(可以对其取地址),是左值;
100和string(”hello”)是临时对象(不能取地址),是右值;
str1是持久对象(可以取地址),是左值。
str1+str2是调用了+操作符,而+操作符返回的是一个string(不可以对其取地址),故其为右值。
m是一个常量引用,引用到一个右值,但引用本身是一个持久对象(可以对其取地址),为左值。
非常量左值引用只能绑定到非常量左值,不能绑定到常量左值,常量右值,非常量右值。如果允许绑定到常量左值和常量右值,则非常量左值引用可以用用于修改常量左值和常量右值,这明显违反了常量的含义。如果允许绑定到非常量右值,这会导致非常危险的情况出现,因为非常量右值是一个临时对象,非常量左值引用可能会使一个已经被销毁的临时对象。
考虑到字符串的链接操作:
string s1("hello");
string s = s1 + "a" + "b" + "c" + "d";
在对s进行初始化时,会产生大量的临时对象,并涉及到大量字符串的拷贝操作,这显然会影响程序的效率和性能。怎么解决这个问题?如果我们能确定某个值是一个非常量右值(或者是一个以后不会再使用的左值),则我们在进行临时对象的拷贝时,可以不用拷贝实际的数据额,而只是“窃取”指向实际数据的指针。c++中引入的右值引用真好是用于标示非常量右值。C++11中使用&&标示右值引用。
c++primer上
左值引用,不能绑定到要求转换的表达式、字面值常量或是返回右值的表达式。
右值引用有着完全相反的特性。
返回左值的函数,连同赋值、下标、解引用和前置递增/递减运算符
返回非引用类型的函数,连同算术、关系、位以及后置递增/递减运算符,都生成右值。
我们不能将一个左值引用绑定到这类表达式上,但是我们可以将一个const的左值引用或者一个右值引用绑定到这类表达式上。
int i = 42;
int &r = i; //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; //错误:表达式是变量
变量是左值,因此我们不能将一个右值引用绑定到一个变量上去,即使这个变量是右值引用类型也不行。
标准库move函数
我们可以显式地将一个左值转换为对应的右值引用类型。我们还可以通过调用一个名为move的标准库函数来获得绑定到左值上的右值引用,此函数定义在utility中。
int &&rr3 = std::move(rr1); //正确
我们必须认识到,调用move就意味着承诺:除了对rr1赋值或是销毁它之外,我们不再使用它。