web-dev-qa-db-ja.com

std :: shared_ptrをstd :: moveするのはなぜですか?

Clangソースコード を見てきましたが、このスニペットが見つかりました:

void CompilerInstance::setInvocation(
    std::shared_ptr<CompilerInvocation> Value) {
  Invocation = std::move(Value);
}

std::movestd::shared_ptrにしたいのはなぜですか?

共有リソースの所有権を譲渡するポイントはありますか?

代わりにこれをやらないのはなぜですか?

void CompilerInstance::setInvocation(
    std::shared_ptr<CompilerInvocation> Value) {
  Invocation = Value;
}
120
sdgfsdh

私は他の答えが十分に強調しなかった一つのことは、speedのポイントだと思います。

std::shared_ptrの参照カウントはatomicです。参照カウントの増減requiresatomicincrementまたはdecrement。これは100倍slowよりもnon-atomicインクリメント/デクリメントではなく、同じカウンタをインクリメントまたはデクリメントすると、正確な数になり、プロセスで膨大な時間とリソースを浪費することになります。

shared_ptrをコピーする代わりに移動することにより、atomic参照カウントを「盗み」、他のshared_ptrを無効にします。参照カウントを「盗む」ことはatomicではなく、shared_ptrをコピーする(そしてを引き起こすよりも100倍高速です) atomic参照の増分または減分)。

この手法は、最適化のためにのみ使用されることに注意してください。 (あなたが提案したように)それをコピーすることは、機能的にも同じように素晴らしいです。

101
David Haim

moveを使用することで、共有の数を増やしてからすぐに減らすことを避けます。これにより、使用回数に関する高価なアトミック操作を節約できます。

112
Bo Persson

std::shared_ptrMove操作(ムーブコンストラクターなど)はcheapです。これは基本的に"stealing pointers"(ソースから)宛先へ;より正確には、状態制御ブロック全体が、参照カウント情報を含め、ソースから宛先へ「盗まれ」ます。

代わりにcopystd::shared_ptrの操作はatomic参照カウントの増加を呼び出します(つまり、整数のRefCountデータメンバーの++RefCountだけでなく、WindowsのInterlockedIncrementの呼び出しなど)単なるポインタ/状態を盗むよりもexpensiveより多く。

したがって、このケースの参照カウントダイナミクスを詳細に分析します。

// shared_ptr<CompilerInvocation> sp;
compilerInstance.setInvocation(sp);

spを値で渡し、CompilerInstance::setInvocationメソッド内でcopyを使用すると、次のようになります。

  1. メソッドに入ると、shared_ptrパラメーターはコピー構成されます:ref countatomicincrement
  2. メソッドの本体内で、データメンバーにshared_ptrパラメーターをcopy入れます:ref countatomicincrement
  3. メソッドを終了すると、shared_ptrパラメーターが破棄されます:ref countatomicdecrement

合計でthreeatomic操作の2つのアトミック増分と1つのアトミック減分があります。

代わりに、値でshared_ptrパラメーターを渡し、メソッド内でstd::moveを渡すと(Clangのコードで適切に行われます)、次のようになります。

  1. メソッドに入ると、shared_ptrパラメーターはコピー構成されます:ref countatomicincrement
  2. メソッドの本体内で、データメンバーにstd::moveパラメーターをshared_ptr入れます:ref count doesnotchange!ポインタ/状態を盗むだけです。高価なアトミックrefカウント操作は必要ありません。
  3. メソッドを終了すると、shared_ptrパラメーターは破棄されます。ただし、ステップ2で移動したため、shared_ptrパラメーターが何も指し示していないため、破棄するものはありません。この場合も、アトミックな減少は発生しません。

結論:この場合、ちょうどoneref count atomic increment、つまりone atomic操作のみを取得します。
ご覧のとおり、これはtwoアトミック増分プラスoneアトミックデクリメントよりもはるかにbetterですコピーケースの場合、合計threeアトミック操作)。

57
Mr.C64

shared_ptrのコピーには、その内部状態オブジェクトポインターのコピーと参照カウントの変更が含まれます。移動には、内部参照カウンターと所有オブジェクトへのポインターの交換のみが含まれるため、高速です。

18
SingerOfTheFall

この状況でstd :: moveを使用する理由は2つあります。ほとんどの応答は速度の問題に対処しましたが、コードの意図をより明確に示すという重要な問題を無視しました。

Std :: shared_ptrの場合、std :: moveはポインティの所有権の移転を明確に示しますが、単純なコピー操作は追加の所有者を追加します。もちろん、その後に元の所有者が所有権を放棄した場合(std :: shared_ptrの破棄を許可するなど)、所有権の譲渡は完了しています。

Std :: moveで所有権を譲渡すると、何が起きているかが明らかです。通常のコピーを使用する場合、元の所有者がすぐに所有権を放棄することを確認するまで、意図した操作が譲渡であることは明らかではありません。ボーナスとして、より効率的な実装が可能です。所有権のアトミック転送により、所有者の数が1増えた(および付随する参照カウントの変更)一時的な状態を回避できるためです。

13