对引用引用的函数的直观理解[重复]
Posted
技术标签:
【中文标题】对引用引用的函数的直观理解[重复]【英文标题】:Intuitive understanding of functions taking references of references [duplicate] 【发布时间】:2011-11-01 12:16:02 【问题描述】:可能重复:What does T&& mean in C++11?
由于某种原因,这超出了我的直觉,我在互联网上找不到任何解释。 C++ 函数获取引用的引用是什么意思?例如:
void myFunction(int&& val); //what does this mean?!
我理解通过引用传递的想法,所以
void addTwo(int& a)
a += 2;
int main()
int x = 5;
addTwo(x);
return 0;
有效,对我来说很直观。
【问题讨论】:
@Fred:我会投票让两者都保持打开状态,您提到的重复项的答案使用 Standardese 说话很重,而下面的 templatetypedef 的答案给出了对使用的简洁解释(但不是真正的定义)通俗地说。 @Matthieu:问题(以及答案)不能合并,因为它们是同一个问题吗?我们不应该保留两个相同的问题。 【参考方案1】:这不是对引用的引用,而是一种称为rvalue reference 的新语言功能,它(非正式地)表示对内存中对象的引用,该对象在程序的其他地方没有被引用,并且可以被破坏性地修改。例如,一个函数的返回值可以被一个右值引用捕获,就像在表达式中引入的临时值一样。
右值引用可用于多种用途。从大多数 C++ 程序员的角度来看,它们可用于实现 move semantics,从而可以通过将旧对象的内容从旧对象“移动”到新对象中来初始化新对象。您可以使用它从 C++11 中的函数返回巨大的对象,而无需支付大量成本来复制对象,因为用于捕获返回值的对象可以使用移动构造函数进行初始化,只需从临时对象中窃取内部信息由 return 语句创建。
移动语义与复制语义正交,因此对象可以移动而不是可复制。例如,std::ofstream
s 不可复制,但它们是可移动的,因此您可以使用移动行为从函数中返回 std::ofstream
s。目前这在 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
置于“清空”状态,然后再将新值放入其中。重要的是,在编写代码以将内容移出对象时,我们要让对象处于某种格式良好的状态,这样代码才能正常工作。
【讨论】:
您能提供一个简单的代码示例来演示它的使用吗? @Daniel- 是的,刚刚做到了!如果还有什么我可以澄清的,请告诉我! 只要确保在被“破坏性修改”之后类必须仍然有效。其中重要的部分是:它应该仍然是可破坏的,并且是可分配的。 @Mooing Duck- 感谢您指出这一点!我刚刚更新了答案以解释这一点以及为什么有必要。 右值引用使用的精彩解释!【参考方案2】:这不是对引用的引用。这是 C++0x 中为所谓的Rvalue references 引入的一种新语法。
【讨论】:
以上是关于对引用引用的函数的直观理解[重复]的主要内容,如果未能解决你的问题,请参考以下文章