在c++03及之前,临时值被认为不可被修改, 跟const T&
无法区分开; c++11引入的一个新类型: 右值引用, 之前只有:
- 左值:
int a = 3;
; - 左值引用:
int a = 3; int& b = a;
,b
是右值引用; - 右值:
3;
, 也是临时值, 包含临时值和纯右值;
1 解决什么问题
- 不必要的复制引起的大量内存操作
在构造和赋值操作的时候, 经常伴随着大量内存拷贝, 有些是必须的,但在有些情况下, 这种隐藏的拷贝会非常浪费,这也是c++长期存在的问题:
1 | template <class T> swap(T& a, T& b) |
- 完美转发 perfect forward
f1(a)
要把参数a
传递给f2(a)
, 可以有以下组织方式:
1 | const int &ref_a = 5; // 编译正常 |
右值引用: 可以指向右值,不能指向左值的引用。
右值引用可以修改右值
右值引用可以指向左值 – std::move
1 | int a = 10; |
std::move 只是强制把左值转换成右值,让右值引用可以指向左值,相当于强制类型转换;
- 从性能上讲,左右值引用没有区别,传参使用左右值引用都可以避免拷贝。
- 右值引用可以直接指向右值,也可以通过std::move指向左值;而左值引用只能指向左值(const左值引用也能指向右值)。
- 作为函数形参时,右值引用更灵活。虽然const左值引用也可以做到左右值都接受,但它无法修改,有一定局限性。
应用场景,实现容器的移动。增加了移动构造函数
、移动赋值重载函数
,
std::move 解析
1 | /** |
std::move是个模板函数,constexpr
表示这个函数是编译期常量,在编译期间求值,不会增加运行时开销;typename
常在模板函数中使用,
告诉编译期,后边跟的std::remove_reference<_Tp>::type
是个类型。
std::remove_reference
有3个定义, 作用是移除引用,返回值本身:
1 | /// remove_reference |
因此return static_cast<typename std::remove_reference<_Tp>::type &&>(__t);
就是强制转换成右值引用返回。
2 完美转发, std::forward
1 | /** |
这里用到了引用折叠,也就是多个引用类型叠加时,最后的类型是什么?引用有左值引用、右值引用,叠加后有4种状态:
- 左引 - 左引: T& & – 左引
- 左引 - 右引: T& && – 左引
- 右引 - 左引: T&& & – 左引
- 右引 - 右引: T&& && – 右引
折叠的规则是: 有1个左引,最后就是左引。
完美转发经典场景:
1 | template <class... Args> |
- std::move 强制转换成右值
- std::forward
是什么值,就转成什么值,中间过渡传递的。它会将输入的参数原封不动地传递到下一个函数中,这个“原封不动”指的是,如果输入的参数是左值,那么传递给下一个函数的参数的也是左值;如果输入的参数是右值,那么传递给下一个函数的参数的也是右值。