# 左值引用和右值引用的区别
# 简要回答
左值引用(T&)绑定到左值(有持久身份的对象),右值引用(T&&)绑定到右值(临时对象或将要销毁的对象)。
右值引用是实现移动语义和完美转发的关键。
简单点说:左值引用给已经存在的变量起了个别名,右值引用专门“捡漏”临时变量的引用,能让资源“偷”过来用而不拷贝,提高效率。
# 详细回答
- 左值引用(T&):
必须绑定到左值(有名称、有内存地址的对象)
生命周期必须长于引用本身,传统C++中唯一的引用类型
常用于函数参数传递以避免拷贝
- 右值引用(T&&):
只能绑定到右值(临时对象、字面量、将亡值)
C++11引入的新特性,主要用途是实现移动语义和完美转发
可以修改所引用的右值(与const左值引用不同)
- 总结:
左值引用只能绑定到实实在在的变量,右值引用专捡临时变量和快要消失的值的"便宜"。
# 适用场景
- 左值引用适用场景:
函数参数传递(避免拷贝)
函数返回多个值(通过引用参数)
操作符重载(如operator<<)
- 右值引用适用场景:
移动构造函数/移动赋值运算符
标准库容器元素插入(如vector::push_back)
工厂函数返回大型对象
完美转发(std::forward)
# 代码示例
#include <iostream>
#include <utility> // for std::move
#include <vector>
void process(int& lref) {
std::cout << "处理左值: " << lref << "\n";
}
void process(int&& rref) {
std::cout << "处理右值: " << rref << "\n";
}
class BigObject {
public:
BigObject() { std::cout << "构造\n"; }
BigObject(const BigObject&) { std::cout << "拷贝构造\n"; }
BigObject(BigObject&&) noexcept { std::cout << "移动构造\n"; }
};
int main() {
// 1. 左值引用示例
int x = 10;
int& lref = x; // 正确:左值引用绑定左值
process(x); // 调用左值版本
// 2. 右值引用示例
process(20); // 调用右值版本
int&& rref = 30; // 右值引用绑定右值
process(std::move(x)); // 转为右值引用
// 3. 移动语义示例
std::vector<BigObject> v;
BigObject obj;
v.push_back(obj); // 调用拷贝构造
v.push_back(BigObject()); // 调用移动构造
v.push_back(std::move(obj)); // 调用移动构造
// 4. 引用折叠示例
auto&& universal = x; // 最终类型是int&
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 知识拓展
拓展
1.引用折叠规则:
T& & → T&
T& && → T&
T&& & → T&
T&& && → T&&
2.万能引用(Universal Reference):
模板参数T&&或auto&&可能是左值引用或右值引用
根据实参类型决定最终类型
配合std::forward实现完美转发
3.std::move本质:
无条件将左值转为右值引用
不移动任何东西,只是类型转换
表示对象可以被移动
- 知识图解

- 面试官可能追问
Q1.什么情况下右值引用会绑定到左值?
使用std::move将左值显式转换为右值引用时
函数返回的局部变量作为右值时
被移动后的对象(处于有效但未指定状态)
Q2.为什么移动构造函数要加noexcept?
标准库容器在重新分配内存时,如果移动操作可能抛出异常,则会选择拷贝而非移动
保证异常安全性
提高代码效率(允许某些优化)
Q3.万能引用和右值引用有什么区别?
万能引用是模板推导上下文中的T&&或auto&&
右值引用是具体类型的X&&
万能引用可以根据实参推导为左值引用或右值引用
评论
验证登录状态...