6

考虑以下代码:

#include <vector>
#include <boost/noncopyable.hpp>

struct A : private boost::noncopyable
{
  A(int num, const std::string& name)
    : num(num),
      name(name)
  {
  }

  A(A&& other)
    : num(other.num),
      name(std::move(other.name))
  {
  }

  int num;
  std::string name;
};

std::vector<A> getVec()
{
  std::vector<A> vec;
  vec.emplace_back(A(3, "foo"));
  // vec.emplace_back(3, "foo"); not available yet in VS10?

  return vec; // error, copy ctor inaccessible
}

int main ( int argc, char* argv[] )
{
  // should call std::vector::vector(std::vector&& other)
  std::vector<A> vec = getVec();

  return 0;
}

这不会在 VS2010 下编译,因为显然Anoncopyable并且因此std::vector<A>无法复制。因此,我不能std::vector<A>从函数中返回 a 。

但是,考虑到 RVO 的概念,我觉得这种事情是不可能的。如果此处应用了返回值优化,则可以省略复制构造并且调用getVec()将有效。

那么这样做的正确方法是什么?这在 VS2010 / C++11 中是否可行?

4

2 回答 2

11

如果return vec;不编译,VS2010 还不完全支持移动语义。通常,如果从函数返回,自动变量会被隐式移动。用作临时解决方法return std::move(vec);,并在脑海中记下以摆脱std::move将来。

完整的解释可以在这个常见问题解答的标题“移出功能”下找到。

此外,您的双参数构造函数会复制通过引用到 const 传递的字符串参数。我建议改为按值取参数并将其移至成员中:

A(int num, std::string name) : num(num), name(std::move(name)) { }

这样,您可以最大限度地减少必要副本的数量。见想要速度?按值传递以获取详细信息。

此外,由于您的移动构造函数没有做任何特别的事情,您可以default

A(A&& other) = default;

这使得它在面对变化时更加稳健。错误很少隐藏在您不编写的代码中:)

于 2012-07-23T08:21:41.023 回答
6

但是,考虑到 RVO 的概念,我觉得这种事情是不可能的。

Elision 是命名返回值优化等东西的总称,是一种优化。这不是必需的。规范允许这样做,但即使允许这样做也不会强制任何实现这样做。

因此,为了在允许省略的编译器和不允许省略的编译器之间强制执行一致性,如果操作允许省略,编译器仍必须验证在代码的当前状态下是否可以省略复制/移动。因此,如果无法访问复制/移动构造函数,即使编译器实际上不会调用它,操作也会失败。

在这种情况下,Visual Studio 2010 在这方面似乎有些困惑。它确实认识到return vec;应该从vec. 不过貌似VS2010的std::vector实现需要一个move赋值操作符才能move;如果没有,它将尝试复制。

于 2012-07-23T08:07:20.490 回答