默认移动操作是否可行?

3

我有一个名为foo的函数:

std::vector<T> foo() {
  std::vector<T> result;
  // populate result
  {
    /*
       for loop with result.push_back().
       ignore real code.
    */
  }
  //
  return result;    <-- note there without std::move
}

如果我按照以下方式分配,它会做多余的复制吗?
const auto v = foo();   <-- will it move the vector by default?
const auto &v = foo();  <-- same question as above, assuming T is movable

我特别关注第二种情况,对于第一种情况,编译器很可能知道要将foo()的结果移动到v中。

const auto &v = foo();

对于这种情况,foo()函数创建了一个临时的result。由于v是一个引用,所以它无法将result移动到v上。因此需要创建一个新的副本。我的理解正确吗?

你是在问NRVO在这里是否适用吗?你可以尝试打印resultv的地址来检查它们是否指向同一位置。据我所见(http://coliru.stacked-crooked.com/a/008e806a76afed61),它们打印的是相同的地址。因此,一个很好的猜测是,没有进行复制。 - badola
对于两种情况都适用吗?对于第二种情况,v 被定义为 const auto &v 吗? - WhatABeautifulWorld
@badola,看起来两种情况下我们都没有额外的复制。我很好奇编译器如何知道第二种情况呢? - WhatABeautifulWorld
const & 允许绑定到临时对象。 - badola
2个回答

3
编译器可以通过应用“as-if规则”进行任何代码转换,只要这些转换不会改变程序的可观察行为。
然而,“复制省略”是“as-if规则”的一个例外:即使这些调用具有可观察的副作用,编译器也可以删除对移动和复制构造函数的调用以及对临时对象的析构函数的匹配调用。要看到这些副作用,您必须在编译时使用-fno-elide-constructors选项。
“复制省略”页面开始,我们应该看一下以下条款:

在返回语句中,当操作数是具有自动存储期限且非易失性对象的名称时,该对象不是函数参数或catch子句参数,并且与函数返回类型相同的类类型(忽略cv-qualification)。这种复制省略的变体称为NRVO,即“命名返回值优化”。

当编译器看到以下结构时,它知道它是NRVO的候选项。

T FunctionName ( ... )
{
    T a;
    ...
    return a;
}

这与您的问题的代码结构相匹配。

std::vector<T> foo() {
  std::vector<T> result;
  // populate result
  {
    /*
       for loop with result.push_back().
       ignore real code.
    */
  }
  return result;
}

案例1 -

const auto v = foo();   <-- will it move the vector by default?

您所见到的并不是移动语义,而只是NRVO。
因此,这个问题在这里没有任何相关性。

情况2 -

const auto &v = foo();  <-- same question as above, assuming T is movable

无论 T 是否可移动,都没有 move 发生。
发生的概念是 const & 可以绑定到临时对象。
在没有移动语义支持的 C++11 之前的编译器中,我们也可以实现相同的结果。

1
NRVO是可选的。 - xskxzr

0

对于这两种情况,从链接看来似乎没有额外的拷贝。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接