【问题标题】:Understanding the code for std::move()理解 std::move() 的代码
【发布时间】:2016-07-12 10:21:44
【问题描述】:

我真的很想了解 std::move() 函数的内在细节。

此处所述的 std::move() 代码:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2027.html#Move_Semantics

template <class T>
typename remove_reference<T>::type&&
move(T&& a)
{
    return a;
}

为什么这段代码返回一个右值引用?该参数是类型 T 右值引用。 a 是否评估右值引用指向的对象,从而将右值引用返回给该对象?另外,为什么需要remove_reference&lt;T&gt; 部分?

教程也写了

这个 move() 给它的目标它的参数的值,但不是 有义务保护其来源的价值。所以,对于一个向量,move() 可以合理地预期将其论点作为零容量 向量以避免复制所有元素。换句话说,移动 是具有潜在破坏性的读取。

但是据说

move 函数确实做的很少。所有的举动都是 接受左值或右值参数,并将其作为右值返回 不会触发复制构造。

这是否应该被理解为,std::move() 可以首先“清空”对象(在本例中为 std::vector),然后将对象作为右值返回(根据 C++ 规范)?但是,官方的 std::move() 是否完全按照后面引用中的说明进行操作?

为什么说 std::move() 在返回右值引用时返回右值?

【问题讨论】:

  • 您正在阅读 2006 年的论文,该论文使用的语言不是 C++11。看一个真正的 C++11 实现中std::move 的定义。
  • 您还将值类别(右值)与类型(右值引用)混淆了。 std::move 返回一个右值(xvalue),它是对T 的右值引用。
  • @Barry:值的类型永远不是引用。正确的说法是move返回一个remove_reference_t&lt;T&gt;类型的xvalue
  • 参数是T型右值引用 it's not
  • @KerrekSB 你是对的。有时候我觉得 C++ 就是这么简单。

标签: c++ c++11 vector stl


【解决方案1】:

此处所述的 std::move() 代码:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2027.html#Move_Semantics

这是 2006 中建议的代码。在移动语义之前是 C++ 的一部分。这不是在 C++ 中实现 std::move 的方式,因为该语言在 2006 年到 2011 年之间发生了显着的变化。如果您想了解 std::move,请查看现代实现,而不是了解一些描述的旧提案相似但不同的东西。

为什么这段代码会返回一个右值引用?

因为这就是std::move 的全部目的。接受一个参数并将其转换为一个右值。

问它为什么这样做就像问为什么make_pair 返回一个pair

参数是一个类型T的右值引用。

其实它是一个“转发引用”,也就是说它可能是一个左值引用,也可能是一个右值引用。

a 是否计算右值引用指向的对象,从而将右值引用返回给该对象?

是的,这就是引用的工作方式。

另外,为什么需要remove_reference&lt;T&gt; 部分?

因为如果std::move 是用左值调用的,那么T 将被推导出为左值引用类型,例如int&amp;。为确保返回类型为int&amp;&amp;,您首先需要删除左值引用以获取int,然后添加&amp;&amp; 以获取右值引用int&amp;&amp;

【讨论】:

  • 谢谢,这真的让我明白了。
  • 只是为了 100% 确定,'a' 评估对象 - 这是一个临时对象 - 并且这个临时对象返回一个右值引用?
  • 不一定是临时的,引用 a 绑定到您调用 std::move 的任何参数。如果我这样做int i=0; std::move(i);,那么它不会绑定到临时的。阅读isocpp.org/blog/2012/11/…以了解“转发引用”(以前称为“通用引用”)
  • 我的意思是表达式“a”会产生一个临时对象,对吧?
  • 我强烈建议您阅读isocpp.org/blog/2012/11/…thbecker.net/articles/rvalue_references/section_01.html,因为它们正确地解释了整个主题。在尝试理解 std::move 或它们如何应用于特定情况之前,最好先正确理解右值引用。
【解决方案2】:

std::move 可以被认为是一个荣耀的演员阵容。它所做的一切都是将它的参数类型(对象或表达式的结果)转换为所谓的xvalue。它通过返回对它的右值引用来实现。

它还考虑到参数的推导类型可能是左值,其中强制转换 T 成为真正的左值引用 - 这就是为什么它必须从 T 中删除引用才能获得“真实”类型的论据。

就是这样。它实际上并没有移动任何东西,清理物体或任何种类。实际的对象修改由相应的移动构造函数或赋值运算符完成。

【讨论】:

  • 这不是真正的“临时”。临时变量是纯右值,而 move 创建一个 xvalue。
  • 嗯,这一切还是很糊涂。对象不是“翻译”的(它是同一个对象!),value 是 - 它从任何东西翻译成 xvalue。此外,对象和值从来都不是引用,因此“动态删除引用”并没有真正的用处。
  • @KerrekSB,还有更好的吗?
  • 嗯,我不知道。它仍然是围绕着黏糊糊的布丁跳舞。我认为 Jon Wakely 的回答清楚而正确地说明了一切。
【解决方案3】:
void f(const my_type&);
void f(my_type&&);
my_type m;
f(m);           // calls f(const my_type&)
f(std::move(m); // calls f(my_type&&)

std::move 的调用唯一要做的就是确保f 的参数类型是右值引用。由f 来决定如何处理该引用。

你可以用演员做同样的事情:

f(my_type&&(m));

但在多个地方使用时,调用std::move 会更清晰、更一致。

【讨论】:

  • 由于参数是表达式,参数的类型永远不能是“引用”。相反,move 确保参数是右值,因此能够绑定到右值引用。
  • 这在形式上是不正确的。就像我在回答中所说的那样, move 确保该值是一个 ravlue(通过返回它)。
  • 撇开争论不谈,这个答案强调了std::move 本身是微不足道且相当无趣的,它所做的是为启用一个动作提供词汇,而不是实际执行一个动作.
猜你喜欢
  • 2016-06-24
  • 2018-07-05
  • 1970-01-01
  • 2014-02-27
  • 1970-01-01
  • 2016-07-08
  • 2014-02-16
  • 1970-01-01
相关资源
最近更新 更多