3.左值和右值
左值和右值
左值(lvalue)和右值(rvalue)是 C++ 中表达式的两种核心分类,其划分依据是表达式是否可以被取地址、是否有持久的存储位置。这个概念是理解 C++ 移动语义、完美转发等高级特性的基础。
一、核心定义与判断标准
1. 左值(lvalue, left value)
核心特征:
- 有持久的存储地址(可以是栈、堆、全局 / 静态存储区);
- 可以用
&取地址; - 通常出现在赋值语句的左边(但不是绝对标准)。
常见例子:
- 变量名、数组元素、函数返回的左值引用(
T&); *ptr(解引用指针)、a[i](数组下标访问)、this指针。
- 变量名、数组元素、函数返回的左值引用(
1 | int a = 10; // a 是左值(有地址,可被取址) |
2. 右值(rvalue, right value)
核心特征:
- 没有持久的存储地址(通常是临时对象、字面量,用完即销毁);
- 不能用
&直接取地址; - 通常出现在赋值语句的右边。
常见例子:
- 字面量(
10、"hello"、true); - 临时对象(如
int(5)、std::string("test")); - 函数返回的非引用类型(如
int func() { return 1; }); - 算术表达式的结果(如
a + b、a * 3)。
- 字面量(
1 | int b = 10 + 20; // 表达式 10+20 是右值(临时结果,无持久地址) |
二、右值的细分(C++11 新增)
C++11 为了支持移动语义,将右值进一步分为两类:
| 类型 | 核心特征 | 常见例子 |
|---|---|---|
| 纯右值(prvalue) | 基础类型的临时值、字面量 | 10、a+b、func()(返回非引用) |
| 将亡值(xvalue) | 有标识,但可以被移动的对象(生命周期即将结束) | 函数返回的右值引用(T&&)、std::move 转换后的对象 |
关键补充:std::move 的作用
std::move 是一个强制类型转换工具,它的作用是将左值强制转换为右值引用(属于将亡值),但它不会移动任何数据,只是赋予了左值 “可以被移动” 的资格。
1 | std::string s = "hello"; |
三、左值引用 vs 右值引用
C++11 引入了右值引用(T&&),与传统的左值引用(T&)对应,二者的核心区别是能绑定的表达式类型不同。
| 引用类型 | 能绑定的表达式 | 核心用途 |
|---|---|---|
左值引用(T&) |
只能绑定左值 | 避免拷贝,修改原对象 |
常量左值引用(const T&) |
能绑定左值 + 右值 | 避免拷贝,只读访问(C++11 前常用) |
右值引用(T&&) |
只能绑定右值 | 实现移动语义,转移临时对象的资源 |
1 | int a = 10; |
四、核心应用场景
1. 移动语义(避免拷贝,提升效率)
对于大对象(如 std::string、std::vector),拷贝操作代价高。右值引用可以直接 “窃取” 临时对象的资源,无需深拷贝。
1 | class MyString { |
2. 完美转发(保持参数的左 / 右值属性)
在模板函数中,通过万能引用(T&&,结合模板参数推导)和 std::forward,可以完美转发参数的左 / 右值属性,这是实现工厂函数、包装函数的关键。
1 | // 万能引用:T&& 可以绑定左值或右值(取决于实参) |
五、关键易错点
“出现在赋值左边” 不是左值的绝对标准
1
2
3
4
5
6// a[i] 是左值,可出现在左边
int arr[5] = {0};
arr[0] = 10;
// ++a 是左值(返回 a 的引用),可出现在左边
int a = 0;
++a = 20; // 合法,最终 a=20右值引用变量本身是左值
右值引用变量一旦被定义,它自己是左值(因为有名字、有地址)。
1
2
3int&& rr = 10;
// int&& rr2 = rr; // 非法:rr 是左值,不能绑定到右值引用
int&& rr2 = std::move(rr); // 合法:转为右值
六、总结
| 对比维度 | 左值 | 右值 |
|---|---|---|
| 取地址 | 可以用 & 取址 |
不能直接取址 |
| 存储 | 有持久存储 | 临时存储,用完即销毁 |
| 引用绑定 | 可被 T&/const T& 绑定 |
可被 const T&/T&& 绑定 |
| 核心用途 | 标识一个持久的对象 | 标识临时对象,支持移动语义 |
左值和右值的核心区别是是否有持久存储地址,而 C++11 对右值的细分(纯右值、将亡值),则是为了支撑移动语义和完美转发,让 C++ 代码更高效。
All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.