在 C++ 中,左值引用(lvalue reference)和 右值引用(rvalue reference)是理解现代 C++ 语义的核心。它们决定了函数重载、移动语义、完美转发等特性如何工作。下面将分块来讲。
一、基本概念
| 引用类型 | 语法 | 绑定对象 | 语义 |
|---|---|---|---|
| 左值引用 | T& | 可寻址的左值 | 传统引用,不能绑定到临时对象 |
| 常量左值引用 | const T& | 左值 + 临时对象 | 可延长临时对象寿命 |
| 右值引用 | T&& | 右值(临时对象) | 用于移动语义、完美转发 |
- 左值(lvalue): 有名字、有地址的对象,例如变量、解引用表达式
*p、数组元素a[i]。 - 右值(rvalue): 临时对象、字面量、运算结果等,不能取地址,如
42、x+1、std::string("hi")。
二、区别与规则
-
绑定规则
T&只能绑定到左值(非临时)。const T&可以绑定到左值和右值(并延长临时寿命至引用生命周期)。T&&只能绑定到右值(临时对象或std::move(x)的结果)。
-
修改权限
T&:可修改所引用对象。const T&:不可修改。T&&:可修改(通常用于“窃取”内部资源)。
-
重载决议
void f(int&); // 接收左值 void f(int&&); // 接收右值 int x = 0; f(x); // 调用 f(int&) f(0); // 调用 f(int&&)
三、右值引用的应用
1. 移动构造与移动赋值
#include <iostream>
#include <vector>
using namespace std;
class Buffer {
int* data;
size_t sz;
public:
Buffer(size_t n): sz(n), data(new int[n]) { cout<<"Ctor\n"; }
~Buffer(){ delete[] data; }
// 移动构造
Buffer(Buffer&& other) noexcept
: data(other.data), sz(other.sz) {
other.data = nullptr;
other.sz = 0;
cout<<"Move Ctor\n";
}
// 禁用拷贝
Buffer(const Buffer&) = delete;
Buffer& operator=(const Buffer&) = delete;
};
int main() {
vector<Buffer> v;
v.emplace_back(100); // 构造
v.push_back(std::move(v[0])); // 移动构造
}
- 意义:避免深拷贝大资源,只“窃取”指针,效率大幅提升。
2. 完美转发
#include <utility>
#include <iostream>
using namespace std;
void g(int& x) { cout<<"lvalue g\n"; }
void g(int&& x) { cout<<"rvalue g\n"; }
template<typename T>
void wrapper(T&& t) {
// 完美转发:保持传入 t 的值类别
g(std::forward<T>(t));
}
int main() {
int a = 1;
wrapper(a); // 输出 lvalue g
wrapper(2); // 输出 rvalue g
}
T&&在模板参数中是 转发引用(forwarding reference),能绑定左/右值,配合std::forward实现完美转发。
四、常见注意事项
-
不要返回局部
T&&T&& bad() { T local; return std::move(local); // 返回悬空引用 } -
对左值使用
std::move后,它变成右值引用,但原对象处于“有效但未指定”状态,谨慎再使用。 -
区分
T&&(右值引用)与模板中的T&&(转发引用):- 非模板上下文:
T&&只能绑定右值。 - 模板上下文:当参数类型是模板参数
T&&时,按 引用折叠规则,可绑定左右值。
- 非模板上下文:
五、示例汇总
#include <iostream>
#include <string>
#include <utility>
using namespace std;
void printRef(const string& s) { cout<<"lvalue ref: "<<s<<"\n"; }
void printRef(string&& s) { cout<<"rvalue ref: "<<s<<"\n"; }
template<typename T>
void relay(T&& t) {
printRef(std::forward<T>(t));
}
int main() {
string s = "hello";
printRef(s); // lvalue ref
printRef("world"s); // rvalue ref
const string cs = "const";
// printRef(cs); // cs 是 const lvalue,匹配 const&
relay(s); // lvalue ref
relay("temp"s); // rvalue ref
// 强制将左值视为右值
printRef(std::move(s)); // rvalue ref
}
通过掌握左值引用与右值引用,你就能:
- 实现高效的移动语义;
- 编写通用且高性能的模板库;
- 优化函数重载,区分临时与持久对象;
- 正确使用
std::move与std::forward,避免不必要的拷贝或悬空引用。

3512

被折叠的 条评论
为什么被折叠?



