7

我试图调整一些代码并将内容从一个向量移动到另一个使用emplace_back()

#include <iostream>
#include <vector>

struct obj
{
  std::string name;

  obj():name("NO_NAME"){}
  obj(const std::string& _name):name(_name){}

  obj(obj&& tmp): name(std::move(tmp.name)) {}
  obj& operator=(obj&& tmp) = default;

};

int main(int argc, char* argv[])
{

  std::vector<obj> v;
  for( int i = 0; i < 1000; ++i )
  {
    v.emplace_back(obj("Jon"));
  }

  std::vector<obj> p;
  for( int i = 0; i < 1000; ++i )
  {
    p.emplace_back(v[i]);
  }

  return(0);
}

这段代码不能用 g++-4.7、g++-4.6 和 clang++ 编译:它有什么问题?

我总是遇到 1 个主要错误

调用 obj 的隐式删除的复制构造函数

?

4

2 回答 2

9

尽管现有答案提供了一种解决方法 usingstd::move使您的程序可以编译,但必须说您的使用emplace_back似乎是基于误解。

您描述它的方式(“我试图 [...] 使用emplace_back()将内容从一个向量移动到另一个向量)以及您使用它的方式表明您将其emplace_back视为一种元素移动到向量中的方法,并且ofpush_back作为将元素复制到向量中的方法。您用来填充向量的第一个实例的代码似乎也暗示了这一点:

std::vector<obj> v;
for( int i = 0; i < 1000; ++i )
{
  v.emplace_back(obj("Jon"));
}

但这不是emplace_back和之间的区别push_back

首先,push_back如果仅给定一个右值并且元素类型具有移动赋值运算符,even会将元素移动(而不是复制)到向量中。

其次,真正的用例是在原地emplace_back构造元素,即当你想将对象放入一个尚不存在的向量时使用它。的参数emplace_back是对象构造函数的参数。所以你上面的循环应该看起来像这样:

std::vector<obj> v;
for( int i = 0; i < 1000; ++i )
{
  v.emplace_back("Jon");   // <-- just pass the string "Jon" , not obj("Jon")
}

您现有代码有效的原因是它obj("Jon")也是构造函数(特别是移动构造函数)的有效参数。但是 的主要思想emplace_back是您无需创建对象然后obj("Jon")将其移入。当您传递而不是传递给它时,您不会从该想法中受益"Jon"

另一方面,在您的第二个循环中,您正在处理之前创建的对象。emplace_back用它来移动已经存在的对象是没有意义的。同样,emplace_back应用于现有对象并不意味着该对象被移动。它只意味着它是使用普通的复制构造函数(如果存在的话)就地创建的。如果你想移动它,只需使用push_back, 应用于结果std::move

std::vector<obj> p;
for( int i = 0; i < 1000; ++i )
{
  p.push_back(std::move(v[i]));  // <-- Use push_back to move existing elements
}

进一步说明
1) 您可以使用 C++11 基于范围的 for 来简化上述循环:

std::vector<obj> p;
for (auto &&obj : v)
  p.push_back(std::move(obj));

2)无论你使用普通的for循环还是基于范围的for,你都一个一个地移动元素,这意味着源向量v将保持为1000个空对象的向量。如果您确实想在过程中清除向量(但仍然使用移动语义将元素传输到新向量),您可以使用向量本身的移动构造函数:

std::vector<obj> p(std::move(v));

这将第二个循环减少到只有一行,并确保源向量被清除。

于 2012-11-25T00:47:42.757 回答
7

问题是

p.emplace_back(v[i]);

将左值传递给emplace_back,这意味着您的移动构造函数(需要右值引用)将不起作用。

如果您确实想将值从一个容器移动到另一个容器,则应显式调用std::move

p.emplace_back(std::move(v[i]));

(移动构造函数背后的想法obj(obj&& tmp)是,它tmp应该是一个不会存在很长时间的对象。在您的第一个循环中,您将一个临时对象传递给emplace_back,这很好 - 右值引用可以绑定到临时对象并从中窃取数据,因为临时对象即将消失。在您的第二个循环中,您传递给的对象emplace_back有一个名称:v[i]。这意味着它不是临时的,并且可以在程序中稍后引用。这就是为什么你必须用std::move它告诉编译器“是的,我真的是想从这个对象中窃取数据,即使其他人以后可能会尝试使用它。 ”)


编辑:我假设你相当不寻常的用法emplace_back是不得不为我们制作一个小例子的遗物。如果不是这种情况,请参阅@jogojapan 的回答,了解为什么使用std::vector移动构造函数或重复调用push_back对您的示例更有意义。

于 2012-11-24T14:05:32.143 回答