左值
左值标识了一个某个对象所在的内存地址(可以对它用&
取地址,所以也叫locator value),它可以出现在等号的左边或者右边,通常表示一个变量
常见的左值有
- 任何类型的变量名
The name of the variable of any type i.e, an identifier of integral, floating, pointer, structure, or union type. -
A[i]
并且A[i]
不表示数组 -
*ptr
并且*ptr
不表示数组 - 上两条错误,数组名也是左值
- const 变量,表示一个nonmodifiable lvalue
-
a.x
或者pa->x
- 一个字符串常量,比如"hello world"是左值(可以取地址)
右值
右值表示一个存在内存某位置的数据值,一个右值不能被另一个值赋值(不能出现在等号左边)
右值相当于将等号右边这个值的生命周期延长到了等号左边这个值的生命周期,实际对应的还是一份对象。
左值可以取地址,右值不可以,所以&12
是错误的
常见的右值有
- 一个临时变量
A a = A();
(当然正常人都是A a()
这么用,这里只是用来举例)- 这里需要注意,编译器会有返回值优化,只会看到调用一次构造函数
- g++添加
-fno-elide-constructors
参数后可以关闭优化,将看到首先调用一个无参的构造函数A()
,返回后将得到一个临时的A的实例,再调用复制构造函数A(const A&)
,这里参数一定要是常量左值引用(reference to const),因为接收的是一个临时值(一个右值)
- 常数
3,4
记住最重要的一点,
左值可以出现在等号左边,右值只能出现在等号右边左值可以取地址,右值不可以
一个对象被用作右值时,使用的是它的内容(值),被当作左值时,使用的是它的地址
一个测试左值右值的方法
template<typename T>
void _check_lrvalue(typename std::remove_reference<T>::type& t) { cout << "lvalue\n"; }
template<typename T>
void _check_lrvalue(typename std::remove_reference<T>::type&& t) { cout << "rvalue\n"; }
template<typename T>
void check_lrvalue(T &&t) { _check_lrvalue<T>(forward<T>(t)); }
int main(int argc, char const *argv[])
{
int a[3];
f2(3);
f2(a[0]);
f2(a);
return 0;
}
refs: