右值引用和移动语义
# 左值与右值
左值引用主要引用左值,左值通常可以修改,比如变量。
右值引用主要引用右值,右值通常不可以修改,比如常量、表达式、函数的返回值及临时变量等,C++11 还将右值区分为纯右值和将亡值。无论左值引用还是右值引用,本质都是给对象取别名。
- 纯右值:基本类型的常量或临时对象,比如:a+b, 100
- 将亡值:自定义类型的临时对象,比如:表达式的中间结果、函数按照值的方式进行返回
左值引用不能直接引用右值,但是const + 左值引用
可以引用右值;右值引用不能直接引用左值,但是可以引用move
后的左值
// 左值引用
int a = 100;
int& b = a;
// 右值引用
int&& c = 100;
const int& d = 100;
int&& e = move(a);
return 0;
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 右值引用的产生
- 左值引用的使用场景:
- 左值引用做参数,防止传参时进行拷贝操作
- 左值引用做返回值,防止返回时对返回对象进行拷贝操作
用左值引用做返回值,并不能完全避免函数返回对象时不必要的拷贝操作。如果函数返回的对象是一个局部变量,该变量出了函数作用域就被销毁了,这种情况下不能用左值引用作为返回值,只能以传值的方式返回,这就是左值引用的短板。
例如:
vector<int> createBigVector() {
std::vector<int> v(1000000, 42);
return v; // C++98会触发拷贝
}
vector<int> vec = createBigVector(); // 性能低下!
1
2
3
4
5
6
2
3
4
5
6
右值引用的解决方案就是给当前类增加移动语义
# 移动语义
C++11 提出了移动语义概念,即:将一个对象中资源移动到另一个对象中的方式,可以有效缓解该问题
例如:
// 移动构造函数
vector(vector&& other) : data_(other.data_), size_(other.size_) {
other.data_ = nullptr; // 置空源对象
}
std::vector<int> vec = createBigVector(); // 效率高
1
2
3
4
5
6
2
3
4
5
6
# 完美转发
完美转发是指在函数模板中,完全依照模板的参数的类型,将参数传递给函数模板中调用的另外一个函数。
所谓完美:函数模板在向其他函数传递自身形参时,如果相应实参是左值,它就应该被转发为左值;如果相应实参是右值,它就应该被转发为右值。这样做是为了保留在其他函数针对转发而来的参数的左右值属性进行不同处理。
void Func(int &x) {
cout << "左值引用" << endl;
}
void Func(const int &x) {
cout << "const 左值引用" << endl;
}
void Func(int &&x) {
cout << "右值引用" << endl;
}
void Func(const int &&x) {
cout << "const 右值引用" << endl;
}
template<class T>
void PerfectForward(T &&t) {
Func(t);
}
int main() {
int a = 10;
PerfectForward(a); //左值
PerfectForward(move(a)); //右值
const int b = 20;
PerfectForward(b); //const 左值
PerfectForward(move(b)); //const 右值
return 0;
}
1
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
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
上次更新: 2025/09/03, 18:26:17