左值、右值
在上一篇中,我们提到了lvalue、xvalue和prvalue的概念。
左和右是相对于等号来说的,在某些简单的场景下,相当于“等号左边的值”和“等号右边的值”,比如
int a;
a = b + c;
a是左值lvalue,有名字,可以取地址,这两点性质是很重要的;
(b + c)这个整体是纯右值prvalue,没有名字,不可以取地址;
将亡值xvalue在前一篇介绍过了,C++11新增的概念。
简单地说,可以用&取地址的是左值,其他都是右值。
“事实上,之所以我们只知道一些关于左值、右值的判断而很少听到其真正的定义的一个原因就是—很难归纳。而且即使归纳了,也需要大量的解释。”
这也正是C++难学的地方。
右值引用
引用是变量的别名(其实底层的实现跟指针一样),C++11之前的引用都是指的左值的别名,现在新增了一种引用,是对右值进行起别名,叫做右值引用,用T &&定义。
右值没有名字,但右值引用是有名字的,右值引用本身其实是个左值。比如张三是个左值,我们给他起个别名叫工人1号,有某个人我们不知道名字,是个右值,我们给他起个别名叫工人2号,工人2号就成了他唯一的名字,我们只能通过工人2号去找到他。
无论是左值引用还是右值引用,必须立刻初始化。这个也好理解,无论是工人1号还是工人2号,首先要有个人,不能是个虚位。
右值引用使右值的生命周期得到了延续,如果没有用右值引用承接,右值就会消亡。比如前面的某个人,如果我们不把他称作工人2号,他很快就消失在历史的尘埃里,我们再也找不到他了。工人2号这个角色赋予他“新生”,但跟工人1号叫张三不一样,没人知道工人2号叫什么名字。
常量左值引用
常量左值引用是个万金油,既可以接受左值,也可以接受右值。比如
const bool a = true;
const bool & b = true;
不考虑编译器优化,二者其实不一样。第一句是先开辟一块匿名空间,放一个true进去,再开辟一块空间名字叫a,把前一块空间的值复制给a代表的空间,再把前一块空间回收。第二句是开辟一块匿名空间,放一个true进去,把这块空间直接起名字叫b。
第一块匿名空间的性质是内容不可变,正是因为b的性质是const,所以可以用作给这块空间取名字,如果把const修饰符去掉,也就是左值引用,是无法作为这块匿名空间的名字的,编译报错。
右值可以赋值给左值,最常见的用法a=1;
右值不能赋值给左值引用,左值引用可以改变里面的值,但右值不可变;
右值可以赋值给常量左值引用;
右值可以赋值给右值引用;
右值的性质是不可变,右值引用和常量左值引用都只是把右值的生命周期延长了,在后面继续读它,并不能改变它。但在一种情况下可以使右值所在空间变成可变的空间,就是下一篇我们要介绍的移动语义,用上一篇提到的移动赋值函数,把右值空间“送”给一个左值。
网友评论