右值引用

左值

左值可以被看作一个具有名称的内存位置。

特征:

  • 能被取地址运算符获取地址
  • 可修改的左值可用作内建赋值和内建复合赋值运算符的左操作数
  • 可用来初始化左值引用

纯右值

相当于 C++ 11 之前的右值。

将亡值

引用

引用 本质是别名,通过引用修改变量的值,传参时避免拷贝。

右值引用

右值引用的标志为 && ,只能指向右值,不能指向左值。

1
2
3
4
5
6
int &&ref_a_right = 5; // ok

int a = 5;
int &&ref_a_left = a; // 编译不过,右值引用不可以指向左值

ref_a_right = 6; // 右值引用的用途:可以修改右值

左值引用与右值引用

1. 右值引用指向左值

使用 std::move

1
2
3
4
5
int a = 5; // a是个左值
int &ref_a_left = a; // 左值引用指向左值
int &&ref_a_right = std::move(a); // 通过std::move将左值转化为右值,可以被右值引用指向

cout << a; // 打印结果:5

std::move 的功能就是强制把左值转换为右值,实现等同于一个类型转换:static_cast<T&& >(lvalue)

2. 左值引用、右值引用是什么值

声明的 左值引用右值引用 都是 左值

万能引用

1
2
3
4
5
6
7
8
9
10
11
12
// 使用 T&& param 的方式,传入左值引用或右值引用,可推导绑定左值或右值
template<typename T>
void func(T&& param) {
cout << param << endl;
}

int main() {
int num = 2019;
func(num);
func(2019);
return 0;
}

引用折叠

一个模板函数,根据定义的形参和传入的实参的类型,我们可以有下面四中组合:

  • 左值-左值 T& & # 函数定义的形参类型是左值引用,传入的实参是左值引用
  • 左值-右值 T& && # 函数定义的形参类型是左值引用,传入的实参是右值引用
  • 右值-左值 T&& & # 函数定义的形参类型是右值引用,传入的实参是左值引用
  • 右值-右值 T&& && # 函数定义的形参类型是右值引用,传入的实参是右值引用

但是 C++中不允许对引用再进行引用,对于上述情况的处理有如下的规则:

所有的折叠引用最终都代表一个引用,要么是左值引用,要么是右值引用。规则是:如果任一引用为左值引用,则结果为左值引用。否则(即两个都是右值引用),结果为右值引用

所以最后结果是:

  • T& & = &
  • T& && = &
  • T&& & = &
  • T&& && = &&

当且仅当函数形参为右值引用且传入参数也为右值引用时,最终折叠后为右值引用

完美转发

使用 std::forward 进行完美转发,std::forward 能够将正确的引用类型转发。

移动语义

在进行对象的复制时,可能有两种需求:一种是深拷贝,开辟新的内存空间,拷贝原对象到这个新的内存空间;另一种则是通过右值引用,将原对象的成员通过修改标记的方式重新由新对象的成员指向,然后将原对象的所有成员清空。

调用赋值运算符时,也同理。

作者

Erial

发布于

2022-09-20

更新于

2023-02-19

许可协议

评论