左值和右值

左值:可以位于赋值操作符左边,有名字且可以取地址的,如变量

右值:只能位于赋值表达式右边,没有名字且不可以取地址的,如常量,临时变量(如a+b)

&10   //错误

a+b=10  //错误

c++中关于右值的性质稍有不同,

1) 对于内置的类型,右值是不可被修改的(non-modifiable),也不可被 const, volatile 所修饰(cv-qualitification ignored)

2) 对于自定义的类型(user-defined types),右值却允许通过它的成员函数进行修改。

在C++11中,右值引用就是对一个右值进行引用的类型。事实上,由于右值通常不具有名字,我们也只能通过引用的方式找到它的存在。通常情况下,我们只能是从右值表达式获得其引用。比如:T && a = ReturnRvalue();这个表达式中,假设ReturnRvalue返回一个右值,我们就声明了一个名为a的右值引用,其值等于ReturnRvalue函数返回的临时变量的值。无论是声明一个左值引用还是右值引用,都必须立即进行初始化。而其原因可以理解为是引用类型本身自己并不拥有所绑定对象的内存,只是该对象的一个别名。左值引用是具名变量值的别名,而右值引用则是不具名(匿名)变量的别名。在上面的例子中,ReturnRvalue函数返回的右值在表达式语句结束后,其生命也就终结了(通常我们也称其具有表达式生命期),而通过右值引用的声明,该右值又“重获新生”,其生命期将与右值引用类型变量a的生命期一样。只要a还“活着”,该右值临时量将会一直“存活”下去。

所以相比于以下语句的声明方式:
T b = ReturnRvalue();

我们刚才的右值引用变量声明,就会少一次对象的析构及一次对象的构造。因为a是右值引用,直接绑定了ReturnRvalue()返回的临时量,而后者是ReturnRvalue()返回值初始化一个T类型的临时变量,而临时变量赋值给b后再析构掉,会多一次析构和构造的开销。

通常情况下,右值引用是不能够绑定到任何的左值的。比如下面的表达式就是无法通过编译的。
int c;
int && d = c;

可以用move(T data)将左值转换为右值。

左值引用是否可以绑定到右值(由右值进行初始化)呢?

T & e = ReturnRvalue();
const T & f = ReturnRvalue();

这样的语句是否能够通过编译呢?这里的答案是:e的初始化会导致编译时错误,而f则不会。

出现这样的状况的原因是,在常量左值引用在C++98标准中开始就是个“万能”的引用类型。它可以接受非常量左值、常量左值、右值对其进行初始化。而且在使用右值对其初始化的时候,常量左值引用还可以像右值引用一样将右值的生命期延长。不过相比于右值引用所引用的右值,常量左值所引用的右值在它的“余生”中只能是只读的。相对地,非常量左值只能接受非常量左值对其进行初始化。

注:无论是左值引用还是右值引用,都减少了资源开销,减少了拷贝过程,直接使目标对象指向源对象,而不用复制一个源对象的副本复制给目标对象

 

posted on 2016-05-19 23:25  小菜鸡y  阅读(306)  评论(0编辑  收藏  举报