一、左值VS右值
在C++中,每一个变量或者表达式,都有一个身份:要么是左值,要么是右值
- 左值:有名字、有固定住址的永久居民
比如:int a = 10;这里的a就是左值。你看可以通过&a找到它的家庭住址
- 右值:没名字、四处漂泊的临时过客
比如10,a + 5,或者一个函数返回的临时对象。它们马上就要消息了,你没法用&取它们的地址
cpp
#include <iostream>
using namespace std;
int main() {
int a = 10; // a 是左值,10 是右值
cout << "a 的地址: " << &a << endl; // ✅ 合法,a 有地址
// cout << &10 << endl; // ❌ 非法,10 没有地址(右值)
// cout << &(a+5) << endl; // ❌ 非法,a+5 的结果没有地址(右值)
return 0;
}
二、引用是什么?
引用就是给变量取个"外号",操作外号就等于操作变量本身
1、左值引用(&):只能给持久居民起外号
bash
int a = 10;
int& ref1 = a; // ✅ 合法,ref1 是 a 的外号
// int& ref2 = 10; // ❌ 非法,不能给临时过客起外号(没住址)
2、常引用(const &):老好人谁都能绑
cpp
int a = 10;
const int& ref3 = a; // ✅ 合法
const int& ref4 = 10; // ✅ 合法!虽然是临时过客,但老好人给它续命了(延长了生命周期)
// ref4 = 20; // ❌ 非法,因为是 const(只读外号)
3、右值引用(&&):专为临时过客准备的外号
cpp
int a = 10;
// int&& ref5 = a; // ❌ 非法,右值引用不能绑持久居民(左值)
int&& ref6 = 10; // ✅ 合法,绑定临时过客(右值)
int&& ref7 = a + 5; // ✅ 合法,绑定临时过客(右值)
注意:右值引用变量自己,是个左值!
这一点极其重要!C++ 规定:只要一个变量有名字,它就是左值 。所以
ref6虽然类型是右值引用,但它自己有名字,它就是个左值
cpp
int&& ref8 = 10;
// int&& ref9 = ref8; // ❌ 非法!因为 ref8 有名字,被当作左值!
int&& ref9 = std::move(ref8); // ✅ 必须用 std::move 把它重新变成右值
三、为什么要有右值引用?
假设你要写一个 String 类,里面有一个指针 char* ptr;。
-
当你把一个左值赋给另一个左值 时,你不能偷别人的数据(别人还要用),所以你只能深拷贝 (重新申请一块内存,把字串复制过去)。这叫拷贝赋值。
-
但是,当你把一个右值(临时对象)赋给另一个左值 时,反正临时对象马上要死了,我们为什么不直接把它的指针"偷"过来 呢?这就叫移动赋值!
四、std::move是什么?
真相:std::move 根本没有移动任何东西!
它就像一个"魔法标签"。你把一个左值传进去,它就给你贴个标签:"虽然它是个左值,但我宣布,从现在起,你就把它当成右值用!"
它的底层实现其实是强制类型转换:
cpp
// 你写:
std::move(a);
// 编译器实际干的事:
static_cast<int&&>(a); // 强行把 a 转成右值引用类型
被 std::move 标记过的左值,它的资源就可以被安全地"偷走"了。所以 std::move 最好只用在"你确定以后不再使用这个变量"的时候