web-dev-qa-db-ja.com

ベクトルが大きくなったときに移動セマンティクスを実施するにはどうすればよいですか?

特定のクラスAのオブジェクトの_std::vector_があります。このクラスは自明ではなく、コピーコンストラクターand移動コンストラクターが定義されています。

_std::vector<A>  myvec;
_

ベクターをAオブジェクトで埋めると(たとえばmyvec.Push_back(a)を使用)、コピーコンストラクターA( const A&)を使用して新しいコピーをインスタンス化して、ベクターのサイズが大きくなります。ベクター内の要素。

クラスAの移動コンストラクターが代わりに使用されていることを何らかの方法で強制できますか?

80

noexceptを使用して、移動コンストラクターとデストラクターがスローしないことをC++(具体的にはstd::vector)に通知する必要があります。次に、ベクトルが大きくなると、移動コンストラクターが呼び出されます。

これは、std::vectorによって尊重される移動コンストラクターを宣言および実装する方法です。

A(A && rhs) noexcept { 
  std::cout << "i am the move constr" <<std::endl;
  ... some code doing the move ...  
  m_value=std::move(rhs.m_value) ; // etc...
}

コンストラクタがnoexceptでない場合、std::vectorはコンストラクタを使用できません。標準で要求される例外保証を保証できないためです。

標準の記述について詳しくは、 C++ Moveのセマンティクスと例外 を参照してください。

例外と関係があるかもしれないとほのめかしたBoに感謝します。また、Kerrek SBのアドバイスを考慮し、可能であればemplace_backを使用してください。 canは高速ですが(多くの場合高速ではありません)、より明確でコンパクトになりますが、いくつかの落とし穴もあります(特に非明示的なコンストラクターの場合)。

Edit、多くの場合、デフォルトはあなたが望むものです:移動可能なものをすべて移動し、残りをコピーします。それを明示的に要求するには、

A(A && rhs) = default;

そうすることで、可能であればnoexceptを取得できます。 デフォルトのMoveコンストラクターはnoexceptとして定義されていますか?

Visual Studio 2015以前の初期バージョンでは、移動セマンティクスがサポートされていますが、これはサポートされていません。

111
Johan Lundberg

興味深いことに、gcc 4.7.2のベクトルは、移動コンストラクターとデストラクターの両方がnoexceptである場合にのみ移動コンストラクターを使用します。簡単な例:

_struct foo {
    foo() {}
    foo( const foo & ) noexcept { std::cout << "copy\n"; }
    foo( foo && ) noexcept { std::cout << "move\n"; }
    ~foo() noexcept {}
};

int main() {
    std::vector< foo > v;
    for ( int i = 0; i < 3; ++i ) v.emplace_back();
}
_

これにより、期待される結果が出力されます。

_move
move
move
_

ただし、~foo()からnoexceptを削除すると、結果が異なります。

_copy
copy
copy
_

これも この質問 に答えると思います。

17
Nikola Benes