这不是对引用的引用,而是一种称为rvalue reference 的新语言功能,它(非正式地)表示对内存中对象的引用,该对象在程序的其他地方没有被引用,并且可以被破坏性地修改。例如,一个函数的返回值可以被一个右值引用捕获,就像在表达式中引入的临时值一样。
右值引用可用于多种用途。从大多数 C++ 程序员的角度来看,它们可用于实现 move semantics,从而可以通过将旧对象的内容从旧对象“移动”到新对象中来初始化新对象。您可以使用它从 C++11 中的函数返回巨大的对象,而无需支付大量成本来复制对象,因为用于捕获返回值的对象可以使用移动构造函数进行初始化,只需从临时对象中窃取内部信息由 return 语句创建。
移动语义与复制语义正交,因此对象可以移动而不是可复制。例如,std::ofstreams 不可复制,但它们是可移动的,因此您可以使用移动行为从函数中返回 std::ofstreams。目前这在 C++03 中无法完成。例如,这段代码在 C++03 中是非法的,但在 C++11 中却完全没问题(并且受到鼓励!):
std::ifstream GetUserFile() {
while (true) {
std::cout << "Enter filename: ";
std::string filename;
std::getline(std::cin, filename);
ifstream input(filename); // Note: No .c_str() either!
if (input) return input;
std::cout << "Sorry, I couldn't open that file." << std::endl;
}
}
std::ifstream file = GetUserFile(); // Okay, move stream out of the function.
直观地说,一个接受右值引用的函数是一个(可能)试图通过将旧对象的内容移动到新对象中来避免昂贵副本的函数。例如,您可以为类向量对象定义 move 构造函数,方法是让该构造函数接受右值引用。如果我们将向量表示为数组指针、数组容量和已用空间的三元组,我们可以如下实现其移动构造函数:
vector::vector(vector&& rhs) {
/* Steal resources from rhs. */
elems = rhs.elems;
size = rhs.size;
capacity = rhs.capacity;
/* Destructively modify rhs to avoid having two objects sharing
* an underlying array.
*/
rhs.elems = nullptr; // Note use of nullptr instead of NULL
rhs.size = 0;
rhs.capacity = 0;
}
重要的是要注意,当我们在构造函数末尾清除 rhs 时,我们最终会将 rhs 置于这样的状态
- 在其析构函数调用时不会导致崩溃(请注意,我们将其元素指针设置为
nullptr,因为释放 nullptr 是安全的),并且
- 仍然允许为对象分配一个新值。后一点很棘手,但重要的是要确保您仍然可以在某个时候为清除的对象赋予新值。这是因为有可能获得一个对象的右值引用,该对象在以后的程序中仍然可以被引用。
为了阐明 (2),右值引用的一个有趣用例是在对象之间显式移动值的能力。例如,考虑swap 的这个惯用实现:
template <typename T> void swap(T& lhs, T& rhs) {
T temp = lhs;
lhs = rhs;
rhs = temp;
}
此代码是合法的,但有点不寻常。特别是,它最终制作了三个副本 - 首先将temp 设置为lhs 的副本,将lhs 设置为rhs 的副本,然后将rhs 设置为rhs 的副本temp。但是我们真的不想在这里复制任何东西。相反,我们只想打乱这些值。因此,在 C++11 中,您将能够使用 std::move 函数显式获取对对象的右值引用:
template <typename T> void swap(T& lhs, T& rhs) {
T temp = std::move(lhs);
lhs = std::move(rhs);
rhs = std::move(temp);
}
现在,根本没有复制。我们将lhs的内容移动到temp,然后将rhs的内容移动到lhs,然后将temp的内容移动到rhs。在这样做的过程中,我们暂时将lhs 和rhs 置于“清空”状态,然后再将新值放入其中。重要的是,在编写代码以将内容移出对象时,我们要让对象处于某种格式良好的状态,这样代码才能正常工作。