导入
右值引用
让我们来学习一下 const 右值引用。
什么是右值引用?
我们先快速回顾一下右值引用。右值引用是在 C++11 中引入的。从那时起,我们将传统的引用(用单个 & 标记)称为左值引用。
通过使用右值引用(&&),我们可以避免不必要的拷贝,转而采用移动语义来转移值的所有权,从而提升性能。但这种方法可能会导致原对象处于不可用状态。
1MyObject a{param1, param2}; 2MyObject b = std::move(a); 3a.foo() // 切勿这样做,这是不安全的,a 可能已处于默认构造状态,甚至更糟
借助右值引用,我们可以减少不必要的拷贝,实现完美转发函数,从而获得更高的性能和更健壮的库设计。
如果我们试图将右值引用与左值引用进行对比,就必须先定义左值引用。我们可以这样说:左值是一个可以取地址的表达式。因此,左值引用是一种定位值的引用。
右值是仅在表达式求值期间存在的无名值。换句话说,我们无法对右值取地址。右值不是左值。
1#include <iostream> 2 3int f() { 4 return 13; 5} 6 7int main() { 8 int i = 42; // i 是一个左值 9 10 int& lvri = i; // lvri 是左值引用 11 12 int&& rvrt = f(); // rvrt 是右值引用,指向 f() 返回的临时右值 13 14 int&& rvrl = 1; // 右值引用指向一个字面量! 15 16 // int&& rv3 = i; // 错误,不能将 int&& 绑定到 int 左值 17 std::cout << i << " " << lvri << " " 18 << rvrt << " " << rvrl << '\n'; 19}
这时我们可能会问:“既然值已经是临时的了,为什么还要去改变它呢?”
然而另一方面,请记住我们说过,右值引用主要用于消除不必要的拷贝,也就是说,它们专门用于移动语义。当我们“移动”某个变量时,意味着该变量会被修改。
那么问题来了:为什么(以及如何)有人会把这样的移动后变量声明为 const 呢?