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

既存の回答std::moveでは、プログラムをコンパイルするための回避策が提供されていますが、の使用はemplace_back誤解に基づいているように思われると言わざるを得ません。

あなたがそれを説明する方法(「私は [...] を使用してコンテンツをベクターから別のベクターに移動しようとしていましたemplace_back() )とその使用方法は、要素をベクターに移動emplace_backする方法として考えていることを示唆しています。の要素をベクターにコピーする方法として。ベクトルの最初のインスタンスを埋めるために使用するコードも、これを示唆しているようです。push_back

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

しかし、これは と の違いではありませemplace_backpush_back

まず、push_back右辺値のみが指定され、要素の型に移動代入演算子がある場合、要素をベクター移動します (コピーしません) 。

第二に、 の実際の使用例は、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"

一方、2 番目のループでは、以前に作成されたオブジェクトを扱っています。を使用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 を使用するかに関係なく、要素を 1 つずつ移動します。つまり、ソース ベクトルvは 1000 個の空のオブジェクトのベクトルとして残ります。プロセスで実際にベクターをクリアしたい場合 (ただし、移動セマンティクスを使用して要素を新しいベクターに転送する場合)、ベクター自体の移動コンストラクターを使用できます。

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

これにより、2 番目のループが 1 行に削減され、ソース ベクターが確実にクリアされます。

于 2012-11-25T00:47:42.757 に答える
7

問題はそれです

p.emplace_back(v[i]);

に左辺値を渡しemplace_backます。これは、移動コンストラクタ (右辺値参照を期待する) が機能しないことを意味します。

実際にあるコンテナから別のコンテナに値を移動したい場合は、明示的に呼び出す必要がありますstd::move:

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

(移動コンストラクター like の背後にある考え方は、それが長く存在しないオブジェクトでobj(obj&& tmp)あるtmpべきだということです。最初のループでは、一時オブジェクトを に渡しますがemplace_back、これは問題ありません。右辺値参照は、一時オブジェクトが消えようとしているので、一時オブジェクトからデータを盗みます. 2 番目のループでは、渡したオブジェクトemplace_backの名前はv[i]. つまり、一時オブジェクトではなく、後でプログラムで参照できるということです. そのため、を使用std::moveしてコンパイラに伝える必要があります


編集:あなたのかなり変わった使い方emplace_backは、私たちのためにちょっとした例を作らなければならないことの遺物だと思います. std::vectorそうでない場合は、@ jogojapan の回答を参照して、ムーブ コンストラクターまたは繰り返し呼び出しを使用することが例にとってより理にかなっている理由についての良い議論を参照push_backしてください。

于 2012-11-24T14:05:32.143 に答える