文章目录
左值(Lvalues)与右值(Rvalues)
定义
Every C++ expression has a type, and belongs to a value category.
左值右值都是表达式属性,该属性称为value category.
在现代C++中,对于左值表达式,通过具体名字和引用来指定一个对象,非左值就是右值。
左值表示程序中必须有一个特定的名字引用到这个值。
An lvalue has an address that your program can access. Examples of lvalue expressions include variable names, including
const
variables, array elements, function calls that return an lvalue reference, bit-fields, unions, and class members.
右值表示程序中没有一个特定的名字引用到这个值。
区分
区分表达式的左右值属性一个简便方法:若可对表达式用 & 符取址,则为左值,否则为右值。
举例:
&obj,&*ptr,&ptr[index],&++x | 有效 |
---|---|
&1729,&(x+y),&std::string(“meow”),&x++ | 无效 |
int x = 0;
++x; // lvalue
x++; // rvalue
++x
修改自身值,并返回自身;x++
先创建一个临时变量,为其赋值,而后修改x
的值,最后返回临时对象。
int x = 0;
++++x; // ok
x++++; // 报错
前++
是直接对对象自增后返回对象,而后++
会先返回记录当前值,再自增,最后返回一个无名的临时对象,那么x++++
就是让第一个后++
返回的无名临时对象再自增,这样对C++是无意义的,所以这样就无法编译通过。
引用
Type&
:只能绑定到可修改的左值表达式
const Type&
: 可以绑定到任何表达式
Type&&
:可绑定到可修改的左值或右值表达式
const Type&&
:可绑定到任何表达式
Error:non-const lvalue reference cannot bind to a temporary
void swap(int& a, int& b) {
// ...
}
int main() {
int a = 10;
int b = 5;
swap(a + 1, b + a);
}
执行上述代码会报如下错误:
Error : non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'
调用swap
函数时,a+1
和a+b
的值会存在一个临时变量中,当把这两个临时变量传递给swap
时,由于swap
的参数是int &
,不是常量引用,因此会产生编译错误。
错误原因:
变量a
在内存中有确切的地址,变量b
在内存中也有确切的地址,但是a+b
却没有,a+b
不是左值(a+1
也不是),只是临时计算一下,然后将得出的结果用于其他语句,之后空间就被释放了,如果绑定一个非const
引用到a+b
相当于一个野指针(引用的本质是一个指针常量),所以是不允许的。
临时变量不能作为非
const
引用参数,是C++编译器的一个关于语义的限制。如果一个参数是以非const
引用传入,C++编译器就有理由认为程序员会在函数中修改这个值,并且这个被修改的引用在函数返回后要发挥作用。但如果把一个临时变量当作非const
引用参数传进来,由于临时变量的特殊性,程序员并不能操作临时变量,而且临时变量随时可能被释放掉,所以,一般说来,修改一个临时变量是毫无意义的,据此,C++编译器加入了临时变量不能作为非const
引用的这个语义限制,意在限制这个非常规用法的潜在错误。
解决方案:
void swap(const int& a, const int& b) {
// ...
}
int main() {
int a = 10;
int b = 5;
swap(a + 1, b + a);
}
将普通引用改为常量引用,改为常量引用后不能修改绑定的对象,只能读不能写。
assigning a temporary object to the const reference extends the lifetime of this object to the lifetime of the const reference
为什么此方案可行?因为常量引用会延长临时变量的生命周期!