私は基本的に把握しようとしていますが、「移動セマンティクス」の概念全体がまったく新しいものなのか、それとも既存のコードを実装しやすくしているだけなのか? コピー/コンストラクターを呼び出す回数を減らすことに常に関心がありますが、通常は参照 (および場合によっては const) を使用してオブジェクトを渡し、常に初期化リストを使用するようにします。これを念頭に置いて(そして醜い && 構文全体を見て)、これらの原則を採用する価値があるのか 、それとも私がすでに行っているように単純にコーディングする価値があるのか 疑問に思いますか?ここで何か新しいことが行われていますか、それとも私がすでに行っていることの「より簡単な」シンタックス シュガーですか?
6 に答える
チューリング タール ピットでは、太陽の下で新しいものは何もありません。ムーブ セマンティクスが行うすべてのことは、ムーブ セマンティクスなしで実行できます。ただ、より多くのコードが必要であり、はるかに脆弱です。
移動セマンティクスが行うことは、多くの状況で効率と安全性を大幅に向上させる特定の共通パターンを採用し、それを言語に埋め込むことです。
明らかな方法で効率が向上します。移動は、それが経由であろうとswap
構築move
であろうと、コピーよりも多くのデータ型ではるかに高速です。いつから移動できるかを示す特別なインターフェイスを作成できますが、正直なところ、人々はそうしませんでした。移動セマンティクスを使用すると、比較的簡単に実行できます。std::vector
a を移動するコストとコピーするコストを比較してください。move
大まかに 3 つのポインターをコピーする必要がありますが、コピーにはヒープの割り当て、コンテナー内のすべての要素のコピー、および 3 つのポインターの作成が必要です。
さらにreserve
、move-awarestd::vector
と copy-only を認識するものをstd::vector
比較してくださいstd::vector
。C++03 では、事前にすべてのコンポーネントのサイズを知らなかった場合、これはパフォーマンスの自殺でした。C++11 では、ムーブ セマンティクスによってシルクのようにスムーズになります。 -vector
外側のベクトルのサイズが変更されるたびに s。
ムーブ セマンティクスにより、すべての「pImpl
パターン」タイプのパフォーマンスが非常に高速になります。これは、値へのポインターを処理および管理する代わりに、値のように動作する複雑なオブジェクトを作成できることを意味します。
これらのパフォーマンスの向上に加えて、複雑なクラスを値として開放することで、ムーブ セマンティクスは多くの安全対策を開放し、以前はあまり実用的ではなかったいくつかのことを実行できるようにします。
std::unique_ptr
の置き換えですstd::auto_ptr
。どちらもほぼ同じことを行いますが、std::auto_ptr
コピーを移動として扱います。これによりstd::auto_ptr
、実際に使用するのはとてつもなく危険になりました。その間、std::unique_ptr
ちょうど動作します。これは、リソースの一意の所有権を非常によく表しており、所有権の譲渡は簡単かつスムーズに行うことができます。
インターフェイスでを取得するという問題を知っています。これは、foo*
「このインターフェイスがオブジェクトの所有権を取得している」ことを意味する場合もあれば、「このインターフェイスがこのオブジェクトをリモートで変更できるようにしたいだけである」ことを意味する場合もあり、詳しく調べる必要があります。 APIドキュメントとソースコードを調べてどちらを見つけますか?
std::unique_ptr
は実際にこの問題を解決します。権限を取得したいインターフェイスは を取得できるようになり、std::unique_ptr<foo>
所有権の譲渡は API レベルとインターフェイスを呼び出すコードの両方で明らかです。 安全でない部分が削除され、移動セマンティクスに置き換えられてstd::unique_ptr
いauto_ptr
ます。そして、これらすべてをほぼ完璧な効率で実行します。
std::unique_ptr
値がポインターによって表されるリソースの転送可能な RAII 表現です。
を書いた後は、非常に低レベルのコードを書いているのでない限り、二度と を直接呼び出さない方make_unique<T>(Args&&...)
がよいでしょう。移動セマンティクスは基本的に時代遅れになっています。new
new
他の RAII 表現は、多くの場合、コピーできません。ポート、印刷セッション、物理デバイスとの対話 - これらはすべて、"コピー" があまり意味をなさないリソースです。それらのほとんどすべては、移動セマンティクスをサポートするように簡単に変更できます。これにより、これらの変数を処理する際の自由度が大幅に広がります。
移動セマンティクスを使用すると、戻り値を関数の戻り部分に入れることもできます。参照によって戻り値を取得するパターン (および「これは out-only、これは in/out」、またはそうしないことを文書化する) は、データを返すことでいくらか置き換えることができます。
の代わりにvoid fill_vec( std::vector<foo>& )
、 がありますstd::vector<foo> get_vec()
。これは、複数の戻り値でも機能しstd::tuple< std::vector<A>, std::set<B>, bool > get_stuff()
ます。呼び出すことができ、 を介してローカル変数にデータを効率的にロードできますstd::tie( my_vec, my_set, my_bool ) = get_stuff()
。
出力パラメータは、セマンティックに出力専用にすることができ、オーバーヘッドはほとんどありません (上記の場合、最悪の場合、bool
これらのコンテナーにあるデータの量に関係なく、8 つのポインターと 2 つのコピーのコストがかかります。そのオーバーヘッドはわずか 0 になる可能性があります)。ポインターと 0bool
のコピー (もう少し作業が必要) は、移動のセマンティクスによるものです。
ここでは絶対に何か新しいことが起こっています。unique_ptr
リソースの所有権を一意に保持しているため、移動できるがコピーできないものを検討してください。その所有権は、必要に応じて新しいものに移動することで転送できますunique_ptr
が、コピーすることはできません (所有するオブジェクトへの参照が 2 つあるため)。
移動の多くの用途はパフォーマンスにプラスの影響を与える可能性がありますが、移動可能だがコピー不可能な型は、言語の機能を大幅に改善します。
要するに、クラスの使用方法の意味を示す新しい手法を使用するか、(重大な) パフォーマンスの問題がコピー アンド 破棄ではなく移動によって軽減できる場合に使用します。
右辺値参照、完全な転送、参照の折りたたみ、およびそれに関連するすべてについて、Thomas Becker の骨の折れる徹底的な記事への参照がなければ、答えは完全ではありません。
ここを参照してください: http://thbecker.net/articles/rvalue_references/section_01.html