什么是左值?
的左值是可以被分配给一个值:
lvalue = rvalue;
这是短期的"左值"或"左侧值",它基本上只是数值上的左边的的=
迹象,即价值分配的东西.
作为不是左值的例子(即仅rvalue):
printf("Hello, world!\n") = 100; // WTF?
该代码不起作用,因为printf()
(返回一个函数int
)不能是左值,只能是右值.
出现在赋值左侧的东西,即可以分配给它的东西.
请注意,在C++中,函数调用可能是左值,如果:
int & func() { static int a = 0; return a; }
然后:
func() = 42; // is legal (and not uncommon)
它传统上是"="运算符的左侧.但是,随着时间的推移,"左值"/"右值"的含义发生了变化.C++添加了一个"不可修改的左值"的术语,它是任何无法赋值的左值:数组和用"const"限定的变量是两个例子.在C中,您不能分配任何右值(见下文).同样,在C++中,您不能分配给某些用户定义的类类型的rvalues.
您可以说"左值"是一个表达式,用于命名随时间持续存在并占据存储位置的对象.是否可以分配该表达式对于该分类并不重要.特别是,引用也是左值,因为它的名称会随着时间的推移而持续存在.以下所有都是左值,因为它们都引用了命名对象.另请注意,a const
对左值没有任何影响.
int a; lvalue: a; lvalue: ++a; int a[2]; lvalue: a; int &ra = a; lvalue: ra; int *pa = &a; lvalue: *pa;
术语"rvalue"用于文字和枚举器值以及不享受长寿的乐趣的临时演员,并且在完整表达结束时立即被销毁.对于右值,不是持久性方面很重要,而是价值方面.C++中的函数是左值,因为它们是持久的并且它们具有地址,即使它们不是对象.我在上面的左值概述中将它们排除在外,因为当首先考虑对象时,更容易掌握左值.以下所有是rvalues:
enum { FOO, BAR }; rvalue: BAR; int a[2]; rvalue: (a+1); rvalue: 42; int a; rvalue: a++; // refering to a temporary struct mystruct { }; mystruct f() { return mystruct(); } rvalue: f();
顺便提一下,通常你有一个左值,但是运算符需要一个右值.例如,二进制内置"+"运算符添加两个值.首先和全部的左值表达式指定首先必须读出值的位置.因此,当您添加两个变量时,会发生"左值到右值"转换.标准表示左值表达式中包含的值是其右值结果:
int a = 0, b = 1; int c = a + b; // read values out of the lvalues of a and b.
其他运营商不采用右值,而是左值.他们没有阅读价值.一个例子是address-of运算符,&
.您不能获取右值表达式的地址.有些rvalues甚至不是对象:它们不占用任何存储空间.例如,文字(10,3.3,...)和枚举值.
好吧,它具有区分左值和右值的几个优点
允许编译器省略为rvalues存储存储并使用寄存器/只读存储器作为标量值
将表达式标记为难以捉摸:rvalues不会长寿
允许编译器在内部和c ++ 1x中有效的复制语义也暴露给程序员(参见移动语义和右值引用):我们可以从可能被销毁的rval中窃取资源.
允许在该属性上构建规则
不允许从左值所指的尚未初始化的对象生成rvalues.但是左值可以指未初始化的对象
rvalues永远不会是多态的.它们的静态类型也必须是它们的动态类型:简化typeid运算符的规则.
......还有更多,我觉得......
我知道的最好的解释之一可以在这篇关于RValue参考的文章中找到.
确定表达式是否为左值的另一种方法是询问"我可以取其地址吗?".如果可以的话,这是一个左值.如果你做不到,这是一个左值.例如,&obj,&*ptr,&ptr [index]和&++ x都是有效的(即使其中一些表达式是愚蠢的),而&1729,&(x + y),&std :: string("喵" ")和&x ++都是无效的.为什么这样做?地址运算符要求其"操作数应为左值"(C++ 03 5.3.1/2).为什么需要呢?获取持久对象的地址是可以的,但是获取临时对象的地址会非常危险,因为临时对象会迅速消失.