129

これが検索結果に表示されないことに驚きました。C++11 の移動セマンティクスの有用性を考えると、誰かが以前にこれを尋ねたと思いました。

C++11 でクラスを移動不可にする必要があるのはいつですか (または、私にとって良い考えですか)?

(既存のコードとの互換性の問題以外の理由、つまり。)

4

4 に答える 4

111

Herb の回答 (編集前) は、実際には移動できないタイプの良い例を示しています: std::mutex.

OS のネイティブミューテックスタイプ ( pthread_mutex_tPOSIX プラットフォームなど) は、オブジェクトのアドレスがその値の一部であることを意味する「位置不変」ではない可能性があります。たとえば、OS は、初期化されたすべてのミューテックス オブジェクトへのポインターのリストを保持している場合があります。ネイティブ OS のミューテックス型がデータ メンバーとして含まれていて、ネイティブ型のアドレスを固定しておく必要がある場合std::mutex(OS がそのミューテックスへのポインターのリストを保持しているため) std::mutex、ネイティブのミューテックス型をヒープに格納する必要があります。std::mutexオブジェクト間を移動したときに同じ場所に移動するか、std::mutex移動してはなりません。これをヒープに格納することはできません。a にはstd::mutexコンストラクconstexprターがあり、グローバルなstd::mutexは、プログラムの実行が開始される前に構築されることが保証されているため、そのコンストラクターは を使用できませんnew。したがって、残された唯一のオプションは、std::mutex不動になることです。

同じ理由が、固定アドレスを必要とするものを含む他の型にも当てはまります。リソースのアドレスを固定しておく必要がある場合は、移動しないでください。

移動しないことには別の議論がありますstd::mutex。それは、移動中にミューテックスをロックしようとしている人がいないことを知る必要があるため、安全に移動するのは非常に難しいということです。ミューテックスはデータ競合を防ぐために使用できるビルディング ブロックの 1 つであるため、競合自体に対して安全でない場合は残念です! 不動std::mutexの場合、構築されてから破棄される前に誰でもできる唯一のことは、ロックとロック解除だけであり、これらの操作はスレッドセーフであることが明示的に保証されており、データ競合が発生しません。これと同じ議論がオブジェクトにも当てはまりstd::atomic<T>ます。オブジェクトをアトミックに移動できない限り、安全に移動することはできません。別のスレッドが呼び出しを試みている可能性があります。compare_exchange_strongオブジェクトが移動している瞬間に。したがって、型が移動可能であってはならないもう 1 つのケースは、型が安全な同時実行コードの低レベルのビルディング ブロックであり、型に対するすべての操作の原子性を保証する必要がある場合です。オブジェクト値がいつでも新しいオブジェクトに移動される可能性がある場合は、アトミック変数を使用してすべてのアトミック変数を保護する必要があるため、それを使用しても安全か、それとも移動されたかを知ることができます...そして保護するアトミック変数そのアトミック変数など...

オブジェクトが純粋なメモリの一部であり、値のホルダーまたは値の抽象化として機能する型ではない場合、それを移動することは意味がないと一般化すると思います。移動できないなどの基本的なタイプint: それらを移動することは単なるコピーです。から内臓を切り取ることはできませんint。その値をコピーしてゼロに設定することはできますが、それでもint値を持つ であり、単なるメモリのバイトです。しかし、intまだ可動ですコピーは有効な移動操作であるためです。ただし、コピー不可能なタイプの場合、メモリの一部を移動したくない、または移動できず、その値もコピーできない場合、それは移動不可能です。ミューテックスまたはアトミック変数は、メモリの特定の場所 (特別なプロパティで処理される) であるため、移動しても意味がなく、コピーもできないため、移動できません。

于 2013-01-13T14:52:52.540 に答える
57

簡単な答え:タイプがコピー可能である場合、それも移動可能である必要があります。ただし、その逆は当てはまりません。のような一部のタイプstd::unique_ptrは移動可能ですが、コピーしても意味がありません。これらは当然移動専用タイプです。

少し長い答えが続きます...

タイプには2つの主要な種類があります(特性などの他のより特殊な目的のものの中で):

  1. intまたはなどの値のようなタイプvector<widget>。これらは値を表し、当然コピー可能である必要があります。C ++ 11では、通常、移動はコピーの最適化と考える必要があります。したがって、すべてのコピー可能なタイプは当然移動可能である必要があります...移動は、よくあるケースでコピーを実行するための効率的な方法にすぎません。元のオブジェクトはもう必要なく、とにかくそれを破壊するだけです。

  2. 基本クラスや仮想または保護されたメンバー関数を持つクラスなど、継承階層に存在する参照のようなタイプ。これらは通常、ポインタまたは参照(多くの場合、base*または)によって保持されるbase&ため、スライスを避けるためにコピー構造を提供しません。既存のオブジェクトと同じように別のオブジェクトを取得したい場合は、通常、のような仮想関数を呼び出しますclone。これらは2つの理由で移動の構築や割り当てを必要としません。コピーできないことと、さらに効率的な自然な「移動」操作がすでにあることです。オブジェクトへのポインタをコピー/移動するだけで、オブジェクト自体は移動しません。新しいメモリ位置に移動する必要があります。

ほとんどのタイプはこれらの2つのカテゴリのいずれかに分類されますが、他の種類のタイプも有用であり、まれです。特にここでは、などのリソースの一意の所有権を表すstd::unique_ptr型は、値に似ていない(コピーするのは意味がない)ため、当然移動専用型ですが、直接使用します(常にではありません)。ポインタまたは参照によって)、このタイプのオブジェクトをある場所から別の場所に移動したい。

于 2013-01-13T11:47:41.800 に答える
18

実際に調べてみると、C++11 のかなりの型が移動できないことがわかりました。

  • すべてのmutexタイプ ( recursive_mutextimed_mutexrecursive_timed_mutex
  • condition_variable
  • type_info
  • error_category
  • locale::facet
  • random_device
  • seed_seq
  • ios_base
  • basic_istream<charT,traits>::sentry
  • basic_ostream<charT,traits>::sentry
  • すべてのatomicタイプ
  • once_flag

どうやら Clang に関する議論があるようです: https://groups.google.com/forum/?fromgroups=#!topic/comp.std.c++/pCO1Qqb3Xa4

于 2013-01-14T04:42:20.150 に答える