什么是右值引用
什么是右值引用
右值引用(Rvalue Reference) 是 C++11 引入的革命性特性,语法为 T&&,它的核心目的是:实现移动语义(Move Semantics)和完美转发(Perfect Forwarding),从而大幅提升程序性能(尤其是对大对象、资源管理类)。
一、前置知识:什么是左值(lvalue)和右值(rvalue)?
| 类型 | 特点 | 示例 |
|---|---|---|
| 左值(lvalue) | 有名字、可取地址、生命周期较长 | int x = 10; → x 是左值 |
| 右值(rvalue) | 无名字、临时对象、即将销毁 | 10、x + 5、func() 返回的临时对象 |
💡 简单判断:
能否出现在赋值号左边?能 → 左值;不能 → 右值。
(注意:C++11 后更精确的分类是:左值、将亡值(xvalue)、纯右值(prvalue),统称 glvalue 和 rvalue)
二、右值引用的语法
Type&& rv_ref = some_rvalue;
T&&表示“对类型 T 的右值引用”。- 只能绑定到右值(临时对象),不能绑定到左值(除非用
std::move)。
示例:
int x = 10;
int&& r1 = 42; // ✅ OK:42 是右值
int&& r2 = x + 5; // ✅ OK:x+5 是右值
int&& r3 = x; // ❌ 错误!x 是左值,不能绑定到右值引用
// 但可以用 std::move 强制转换左值为右值
int&& r4 = std::move(x); // ✅ OK:std::move(x) 是右值
⚠️ 注意:
std::move不移动任何东西!它只是类型转换,把左值转为右值引用(以便调用移动构造函数)。
三、核心用途 1:移动语义(Move Semantics)
目标:避免不必要的深拷贝,直接“窃取”资源
传统问题(C++98/03):
std::vector createHugeVector() {
std::vector v(1000000); // 分配大内存
return v; // 返回时触发拷贝构造 → 深拷贝 100 万个 int!
}
即使编译器做 RVO(返回值优化),也无法覆盖所有场景。
C++11 解决方案:移动构造函数 + 移动赋值运算符
#include
#include
class MyString {
private:
char* data;
public:
// 构造函数:从 C 风格字符串创建
MyString(const char* str = "") {
size_t len = std::strlen(str);
data = new char[len + 1];
std::strcpy(data, str);
std::cout << "【构造】分配内存: " << data << " ("" << data << "")
";
}
// 拷贝构造函数(深拷贝)
MyString(const MyString& other) {
size_t len = std::strlen(other.data);
data = new char[len + 1];
std::strcpy(data, other.data);
std::cout << "【拷贝构造】深拷贝到: " << data << "
";
}
// 移动构造函数(接管资源)
MyString(MyString&& other) noexcept {
data = other.data; // 直接拿走对方的指针
other.data = nullptr; // 让对方变“空”
std::cout << "【移动构造】接管指针: " << data << "
";
}
// 拷贝赋值(深拷贝)
MyString& operator=(const MyString& other) {
if (this != &other) {
delete[] data;
size_t len = std::strlen(other.data);
data = new char[len + 1];
std::strcpy(data, other.data);
std::cout << "【拷贝赋值】深拷贝到: " << data << "
";
}
return *this;
}
// 移动赋值(接管资源)
MyString& operator=(MyString&& other) noexcept {
if (this != &other) {
delete[] data; // 先释放自己的旧资源
data = other.data; // 接管对方资源
other.data = nullptr; // 让对方变空
std::cout << "【移动赋值】接管指针: " << data << "
";
}
return *this;
}
// 析构函数
~MyString() {
if (data) {
std::cout << "【析构】释放内存: " << data << "
";
delete[] data;
} else {
std::cout << "【析构】空指针,无需释放
";
}
}
// 打印内容(用于测试)
void print() const {
std::cout << "String: "" << (data ? data : "nullptr") << ""
";
}
};
// 这个函数返回一个 MyString 临时对象(右值!)
MyString createString() {
MyString temp("Hello from function!");
std::cout << " → 函数内创建临时对象
";
return temp; // 返回时,temp 是局部变量,但会被“移动”出去(C++11+)
}
int main() {
std::cout << "=== 1. 使用移动构造(返回临时对象) ===
";
MyString s1 = createString(); // ← 这里会调用 移动构造函数!
s1.print();
std::cout << "
=== 2. 再次赋值一个临时对象 ===
";
s1 = createString(); // ← 这里会调用 移动赋值运算符!
s1.print();
std::cout << "
=== 3. 对比:如果禁用移动(强制拷贝) ===
";
const MyString source("Original");
MyString s2 = source; // ← 调用 拷贝构造(因为 source 是左值!)
s2.print();
return 0;
}
=== 1. 使用移动构造(返回临时对象) ===
【构造】分配内存: 0x12345678 ("Hello from function!")
→ 函数内创建临时对象
【移动构造】接管指针: 0x12345678
【析构】空指针,无需释放 ← 这是函数内 temp 的析构(已被置空)
String: "Hello from function!"
=== 2. 再次赋值一个临时对象 ===
【构造】分配内存: 0x87654321 ("Hello from function!")
→ 函数内创建临时对象
【移动赋值】接管指针: 0x87654321
【析构】空指针,无需释放 ← 函数内 temp 析构
【析构】释放内存: 0x12345678 ← s1 原来的内存被释放(在移动赋值中 delete[])
String: "Hello from function!"
=== 3. 对比:如果禁用移动(强制拷贝) ===
【构造】分配内存: 0xabcdef00 ("Original")
【拷贝构造】深拷贝到: 0x98765432
String: "Original"
四、核心用途 2:完美转发(Perfect Forwarding)
在泛型编程中,我们希望保持参数的原始值类别(左值/右值) 传递给另一个函数。
问题:
template
void wrapper(T param) {
foo(param); // 无论传入左值还是右值,param 都是左值!
}
解决方案:万能引用(Universal Reference) + std::forward
template
void wrapper(T&& param) { // T&& 在模板中是“万能引用”
foo(std::forward(param)); // 完美转发:保留原始值类别
}
int x = 10;
wrapper(x); // x 是左值 → foo 接收左值
wrapper(42); // 42 是右值 → foo 接收右值
🔑 关键:
T&&在模板中不是“右值引用”,而是 “转发引用(Forwarding Reference)”,可绑定左值或右值。





