web-dev-qa-db-ja.com

std :: unique_ptrをstd :: vectorにプッシュバックしてもコンパイラーは失敗しません

_unique_ptr_を使用しない限り、_std::vector_はコピーできないため、_std::move_に戻すことはできません。ただし、Fを_unique_ptr_を返す関数とすると、操作std::vector::Push_back(F())が許可されます。以下に例があります:

_#include <iostream>
#include <vector>
#include <memory>

class A {
  public:
    int f() { return _f + 10; }

  private:
    int _f = 20;
};

std::unique_ptr<A> create() { return std::unique_ptr<A>(new A); }


int main() {
  std::unique_ptr<A> p1(new A());

  std::vector< std::unique_ptr<A> > v;

  v.Push_back(p1); // (1) This fails, should use std::move

  v.Push_back(create()); // (2) This doesn't fail, should use std::move?

  return 0;
}
_

_(2)_は許可されますが、_(1)_は許可されません。これは、戻り値が何らかの方法で暗黙的に移動されるためですか?

_(2)_では、実際に_std::move_を使用する必要がありますか?

22
Dan

std::move(X)は本質的に「ここでは、Xを一時オブジェクトであるかのように扱う」ことを意味します。

create()は、最初に一時的な_std::unique_ptr<A>_を返すため、moveは不要です。


詳細を知りたい場合は、 値のカテゴリ を調べてください。コンパイラーは、値カテゴリーを使用して、式が一時オブジェクトを参照するか(「rvalue」)参照しないか(「lvalue」)を判別します。

_p1_は左辺値であり、create()は右辺値です。

34
HolyBlackCat

std::vector::Push_back() 右辺値参照を入力として受け取るオーバーロードがあります。

_void Push_back( T&& value );
_

create()の戻り値は名前のない一時変数、つまり右辺値であるため、Push_back()を使用する必要なく、そのままstd::move()に渡すことができます。

std::move()は、名前付き変数、つまり右辺値が期待される左辺値を渡す場合にのみ必要です。

8
Remy Lebeau

C++ 11では、移動コンストラクタと右辺値のセマンティクスを取得しました。

std :: move(X)は、XをX &&に変換する右辺値へのキャストです。 move ctorがジョブを引き継ぎ、moveコンストラクターは通常、引数によって保持されているリソースを「スチール」します。 unique_ptrには移動トラクターがあります。

関数の戻り値はすでに右辺値です(関数がコメントの@HolyBlackCatで示されているように左辺値参照を返さない場合)。これにより、余分なキャストを必要とせずにムーブctorがトリガーされます。そしてmove ctorはunique_ptrに対して定義されているため、コンパイルされます。

また、v.Push_back(p1); failingの理由は、左辺値を使用してコピーコンストラクターを呼び出そうとしましたが、unique_ptrにコピーctorがないために失敗します。

6

コンパイラーが明示的には移動しないオブジェクトを移動する機能のためにも機能することを知っておく価値があります(NRVO)

#include <iostream>
#include <vector>
#include <memory>

class A {
  public:
    int f() { return _f + 10; }

  private:
    int _f = 20;
};

std::unique_ptr<A> create() {
    std::unique_ptr<A> x (new A);
    return x; 

}


int main() {
  std::unique_ptr<A> p1(new A());

  std::vector< std::unique_ptr<A> > v;

  //v.Push_back(p1); // (1) This fails, should use std::move

  v.Push_back(create()); // (2) This doesn't fail, should use std::move?

  return 0;
}
2
NewMe