【问题标题】:Returning std::vector with std::move用 std::move 返回 std::vector
【发布时间】:2017-10-19 08:00:29
【问题描述】:

我有一个非常基本的问题:使用std::move 返回std::vector<A> 是个好主意吗?例如:

class A {};
std::vector<A> && func() {
    std::vector<A> v;
    /* fill v */
    return std::move(v);
}

我应该以这种方式返回std::mapstd::list..等吗?

【问题讨论】:

  • 这不会编译。
  • 按值返回即可。
  • std::vector&lt;A&gt; &amp; v; ??你确定吗?
  • 按值返回应该被认为是可取的。您将获得返回值优化,而移动您将招致移动。
  • 我的意思是 std::vector v,没有 &

标签: c++ return move


【解决方案1】:

你声明了一个通过右值引用返回的函数——这几乎不应该这样做(如果你通过引用返回本地对象,你最终会得到一个悬空引用)。而是将函数声明为按值返回。这样,调用者的值将由函数返回的 r 值构造。返回的值也将绑定到任何引用。

其次,不,您应该使用显式的std::move 返回,因为这会阻止编译器使用RVO。没有必要,因为编译器会自动将返回的任何左值引用转换为右值引用。

std::vector<A> func() {
    std::vector<A> v;
    /* fill v */
    return v; // 'v' is converted to r-value and return value is move constructed.
}

更多信息:

【讨论】:

  • "RVO 优化"?
  • @Apollys 已编辑。谢谢。
  • 直到 C++14 才成立。在 C++11 中,当复制构造函数是已删除的函数时,您需要显式地 std::move ,因为不能保证 RVO 优化。出于这个原因,编译器需要有一个复制构造函数,以便他在决定不能应用 RVO 时选择复制
  • @HAL9000 这个答案是关于std::vectorstd::mapstd::list(请参阅问题),它们的复制ctor 都没有被删除。当然还有其他对象需要明确的std::move,例如std::unique_ptr 等。在设计不允许复制的情况下,使用显式移动表示资源所有权转移,而不是优化返回值。顺便说一句,在 C++11 时代,基本上所有的编译器都支持 RVO,尽管标准没有保证:)
  • @Snps 我同意第一部分,我没有足够重视这个问题。对于 RVO 支持,您没有理解重点:当复制构造函数是一个已删除的函数时,语言认为这是一个错误,因为不保证 RVO 支持,编译器是否支持它并不重要。
【解决方案2】:

不,不是。这实际上会在某些情况下防止复制省略。一些编译器甚至有一个关于它的警告,称为-Wpessimizing-move

与其他答案一致,只需按值返回,将返回类型更改为简单的std::vector&lt;A&gt;,编译器将在需要时负责调用移动构造函数。

你可以看看我刚刚找到的这篇文章,它似乎解释得很详细(虽然我自己没有通读过): https://vmpstr.blogspot.hu/2015/12/redundant-stdmove.html

【讨论】:

  • 虽然在悲观的举动上是正确的,但这个答案很危险,因为它无法解决返回的引用将悬空的事实。
  • 你不认为说“按价值返回”之类的说法可以解决这个问题吗?
  • 我认为这有点太微妙了:它可以解释为“只返回没有std::move 的值”以及“更改返回类型”。
【解决方案3】:

当您返回局部变量和编写std::move() 时,启用优化的 gcc 和 clang 编译器都会为这两种情况生成相同的二进制代码。 只需返回一个值。

但是,如果您为自定义类创建移动构造函数并移动 operator=,则使用 &amp;&amp;noexcept 说明符很有用

【讨论】:

  • 这与优化无关,真的。在这种情况下,标准要求首先尝试移动。此外,这个答案很危险,因为它无法解决返回的引用将悬空的事实。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-06-04
  • 2019-03-05
  • 1970-01-01
  • 1970-01-01
  • 2019-07-09
  • 2013-04-05
相关资源
最近更新 更多