这不是参考的参考,而是一个新的语言功能,称为右值引用 //www.artima.com/cppsource/rvalue.html它(非正式地)表示对内存中对象的引用,该对象在程序的其他地方未引用并且可以被破坏性修改。例如,函数的返回值可以通过右值引用捕获,就像引入到表达式中的临时值一样。
右值引用可用于多种目的。从大多数C++程序员的角度来看,它们可以用来实现移动语义 //www.stroustrup.com/C++11FAQ.html#rval,由此可以通过将旧对象的内容从旧对象“移出”到新对象中来初始化新对象。您可以使用它从 C++11 中的函数返回巨大的对象,而无需花费巨大的成本来复制该对象,因为用于捕获返回值的对象可以使用移动构造函数进行初始化,只需从临时对象中窃取内部结构由 return 语句创建。
移动语义与复制语义正交,因此对象可以移动但不可复制。例如,std::ofstream
s 不可复制,但它们是可移动的,因此您可以返回std::ofstream
来自使用移动行为的函数。目前在 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.
直观上,采用右值引用的函数(可能)试图通过将旧对象的内容移动到新对象中来避免昂贵的复制。例如,您可以定义一个移动构造函数对于类似向量的对象,通过让该构造函数接受右值引用。如果我们将向量表示为数组指针、数组容量和已使用空间的三元组,我们可以按如下方式实现其移动构造函数:
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
成为的副本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
into temp
,然后移动内容rhs
into lhs
,然后移动内容temp
into rhs
。在这样做的过程中,我们离开了lhs
and rhs
在将新值放入其中之前暂时处于“清空”状态。重要的是,在编写将内容移出对象的代码时,我们要使对象处于某种格式良好的状态,以便该代码正常工作。