值类别
每个 C++ 表达式(运算符带上其操作数、字面量、变量名等)可按照两种独立的性质加以辨别:类型和值类别 (value category)。每个表达式都具有某种非引用类型,且每个表达式恰属于三种基本值类别之一:纯右值 (prvalue)、亡值 (xvalue)、左值 (lvalue),定义如下:
- 泛左值 (glvalue)(“泛化 (generalized)”的左值)是其求值确定一个对象、位域或函数的个体的表达式;
- 纯右值 (prvalue)(“纯 (pure)”的右值)是求值符合下列之一的表达式:
- 计算某个运算符的操作数的值(这种纯右值没有结果对象),或者
- 初始化某个对象或位域(称这种纯右值有一个结果对象)。所有类和数组的纯右值都有结果对象,即使它被舍弃也是如此。在某些语境中,将发生临时量实质化,以创建作为其结果对象的临时量;
- 亡值 (xvalue)(“将亡 (expiring)”的值)是代表其资源能够被重新使用的对象或位域的泛左值;
- 左值 (lvalue)(如此称呼的历史原因是,左值可以出现于赋值表达式的左边)是非亡值的泛左值;
- 右值 (rvalue)(如此称呼的历史原因是,右值可以出现于赋值表达式的右边)是纯右值或者亡值。
关系图
示例
以下示例演示左值和右值的多种正确的和错误的用法:
// lvalues_and_rvalues2.cpp
int main()
{
int i, j, *p;
// Correct usage: the variable i is an lvalue and the literal 7 is a prvalue.
// 用法正确:变量i是左值 ,文本7是纯右值 。
i = 7;
// Incorrect usage: The left operand must be an lvalue (C2106).`j * 4` is a prvalue.
// 用法不正确:左操作数必须是左值(C2106)。'j*4'是纯右值。
7 = i; // C2106
j * 4 = 7; // C2106
// Correct usage: the dereferenced pointer is an lvalue.
// 用法正确:未引用的指针是左值。
*p = i;
// Correct usage: the conditional operator returns an lvalue.
// 用法正确:条件运算符返回左值。
((i < 3) ? i : j) = 7;
// Incorrect usage: the constant ci is a non-modifiable lvalue (C3892).
// 用法错误:常量ci是不可修改的左值(c3892)。
const int ci = 7;
ci = 9; // C3892
}
左值引用声明符: &
保留对象的地址,但行为方式在语法上与对象相似。
语法
type-id & cast-expression
示例
以下示例通过声明 Person 对象和对该对象的引用演示了引用声明符。 由于 rFriend 是对 myFriend 的引用,因此更新任一变量都将更改同一对象。
int main()
{
// 正确:i是左值,x是左值引用
int i = 10;
int& x = i;
cout << "i:" << i << " x:" << x << endl;
i = 20;
x = 30;
cout << "i:" << i << " x:" << x << endl;
// 错误:非常量引用的初始值必须为左值
//int& x = 10; // 10是右值
// 正确
const int& c = 10;
}
Output |
---|
i:10 x:10 i:30 x:30 |
反汇编
int i = 10;
00BE5DB2 mov dword ptr [i],0Ah
int& x = i;
00BE5DB9 lea eax,[i]
int& x = i;
00BE5DBC mov dword ptr [x],eax
i = 20;
00BE5E22 mov dword ptr [i],14h
x = 30;
00BE5E29 mov eax,dword ptr [x]
00BE5E2C mov dword ptr [eax],1Eh
伪代码
int main()
{
int i = 10;
int* x = &i;
cout << "i:" << i << " x:" << *x << endl;
i = 20;
*x = 30;
cout << "i:" << i << " x:" << *x << endl;
}
右值引用声明符: &&
保留对右值表达式的引用。
语法
type-id && cast-expression
示例
#include <iostream>
using namespace std;
int main()
{
// 错误:无法将右值引用绑定到左值
//int i = 10; // i是左值
//int&& x = i;
// 正确
int&& x = 10;
cout << "x:" << x << endl;
x = 20;
cout << "x:" << x << endl;
}
Output |
---|
x:10 x:20 |
反汇编
int&& x = 10;
00DA5DB2 mov dword ptr [ebp-18h],0Ah
00DA5DB9 lea eax,[ebp-18h]
00DA5DBC mov dword ptr [x],eax
x = 20;
00DA5E00 mov eax,dword ptr [x]
00DA5E03 mov dword ptr [eax],14h
伪代码
int main()
{
int temp = 10;
int* x = &temp;
cout << "x:" << *x << endl;
*x = 20;
cout << "x:" << *x << endl;
}