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

Opo*_*sum 6 c++ visual-studio-2010 visual-c++ move-semantics c++11

请考虑以下代码:

#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;
}
Run Code Online (Sandbox Code Playgroud)

这并不在VS2010编译,因为很明显Anoncopyable,因此std::vector<A>无法复制.因此我不能std::vector<A>从函数返回一个.

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

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

fre*_*low 11

如果return vec;不编译,VS2010不支持完全移动语义.通常,如果从函数返回,则自动变量会隐式移动.使用return std::move(vec);作为临时解决办法,并记在你的头上,以摆脱std::move在未来.

有关完整说明,请参阅此常见问题解答中的标题为"退出功能".

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

A(int num, std::string name) : num(num), name(std::move(name)) { }
Run Code Online (Sandbox Code Playgroud)

这样,您可以最大限度地减少必要副本的数量.看速度快?通过值传递详细信息.

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

A(A&& other) = default;
Run Code Online (Sandbox Code Playgroud)

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

  • @Opossum嗯,然后继续等待,VS2012也没带来.它没有带来很多新东西,甚至没有像拖欠和删除函数或委托和继承构造函数这样相当简单的东西. (3认同)

Nic*_*las 6

然而,考虑到RVO的概念,我觉得这样的事情是不可能的.

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

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

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

  • 错了,通常矢量应该在函数中自动移动. (2认同)