C ++ 11 / VS2010:返回不可复制但可移动的对象的容器

时间:2012-07-23 08:00:01

标签: c++ visual-studio-2010 visual-c++ c++11 move-semantics

请考虑以下代码:

#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>

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

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

2 个答案:

答案 0 :(得分:11)

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

在“退出职能”标题下可以找到in this FAQ answer的完整说明。

此外,您的双参数构造函数会生成由reference-to-const传递的字符串参数的副本。我建议改为以值为基础并将其移入成员:

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

这样,您可以最大限度地减少必要副本的数量。有关详细信息,请参阅Want Speed? Pass by Value

此外,由于您的移动构造函数没有做任何特殊操作,您可以default

A(A&& other) = default;

这使得它在面对变化时更加强大。错误很少隐藏在你不写的代码中:)

答案 1 :(得分:6)

  

然而,考虑到RVO的概念,我认为这种事情是不可能的。

Elision,像命名返回值优化这样的东西的通用术语,是一种优化。它不是必需。规范允许它,但即使允许,也不会强制执行任何实现。

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

在这种情况下,Visual Studio 2010似乎在这方面有点混乱。它确实认识到return vec;应该从vec移出。但是,似乎VS2010的std::vector实现需要一个移动赋值运算符来移动;没有一个,它会尝试复制。