重複の可能性:
移動のセマンティクスを説明してもらえますか?
誰かが私に良い情報源を指摘したり、移動のセマンティクスとは何かをここで説明したりできますか?
重複の可能性:
移動のセマンティクスを説明してもらえますか?
誰かが私に良い情報源を指摘したり、移動のセマンティクスとは何かをここで説明したりできますか?
C++0x のことはしばらく忘れてください。移動セマンティクスは言語に依存しないものです。C++0x は、移動セマンティクスを使用して操作を実行する標準的な方法を提供するだけです。
移動セマンティクスは、特定の操作の動作を定義します。ほとんどの場合、これらはコピー セマンティクスと対比されるため、最初に定義しておくと便利です。
コピー セマンティクスを使用した割り当てには、次の動作があります。
// Copy semantics
assert(b == c);
a = b;
assert(a == b && b == c);
つまりa
に等しくなりb
、 は変更しb
ないままにします。
移動セマンティクスを使用した割り当てでは、事後条件が弱くなります。
// Move semantics
assert(b == c);
move(a, b); // not C++0x
assert(a == c);
b
移動セマンティクスを使用した代入後に変更されないという保証はもはやないことに注意してください。これが決定的な違いです。
移動セマンティクスの利点の 1 つは、特定の状況で最適化できることです。次の通常の値の型を検討してください。
struct A { T* x; };
また、メンバーが等しい値を指している場合、型A
が等しい2 つのオブジェクトを定義すると仮定します。x
bool operator==(const A& lhs, const A& rhs) { return *lhs.x == *rhs.x; }
最後に、メンバーA
のポインティに対して唯一の所有権を持つようにオブジェクトを定義すると仮定します。x
A::~A() { delete x; }
A::A(const A& rhs) : x(new T(rhs.x)) {}
A& A::operator=(const A& rhs) { if (this != &rhs) *x = *rhs.x; }
A
ここで、2 つのオブジェクトを交換する関数を定義したいとします。
コピー セマンティクスを使用して、通常の方法でそれを行うことができます。
void swap(A& a, A& b)
{
A t = a;
a = b;
b = t;
}
ただし、これは不必要に非効率的です。私たちは何をしていますか?
a
intoのコピーを作成しますt
。b
しa
ます。t
しb
ます。t
ます。T
オブジェクトのコピーにコストがかかる場合、これは無駄です。あなたのコンピュータ上の 2 つのファイルを交換するように頼んだ場合、3 番目のファイルを作成してから、一時ファイルを破棄する前にファイルの内容をコピー アンド ペーストすることはありませんよね? いいえ、1 つのファイルを移動し、2 番目のファイルを最初の位置に移動し、最後に最初のファイルを 2 番目の位置に戻します。データをコピーする必要はありません。
私たちの場合、 type のオブジェクトを移動するのは簡単ですA
:
// Not C++0x
void move(A& lhs, A& rhs)
{
lhs.x = rhs.x;
rhs.x = nullptr;
}
rhs
のポインターを に移動し、そのポインターの所有権をlhs
放棄するrhs
(null に設定する) だけです。これは、移動セマンティクスのより弱い事後条件が最適化を可能にする理由を明らかにするはずです。
この新しい移動操作を定義すると、最適化されたスワップを定義できます。
void swap(A& a, A& b)
{
A t;
move(t, a);
move(a, b);
move(b, t);
}
移動セマンティクスのもう 1 つの利点は、コピーできないオブジェクトを移動できることです。これの代表的な例はstd::auto_ptr
.
C++0x では、右辺値参照機能を使用してセマンティクスを移動できます。具体的には、次のような操作:
a = b;
b
が右辺値参照 (スペルト) の場合は移動セマンティクスT&&
を持ち、それ以外の場合はコピー セマンティクスを持ちます。が右辺値参照でない場合、関数 (以前に定義したstd::move
関数とは異なります)を使用して移動セマンティクスを強制できます。move
b
a = std::move(b);
std::move
基本的にその引数を右辺値参照にキャストする単純な関数です。式 (関数呼び出しなど) の結果は自動的に右辺値参照になるため、コードを変更せずにそのような場合にムーブ セマンティクスを利用できることに注意してください。
移動の最適化を定義するには、移動コンストラクターと移動代入演算子を定義する必要があります。
T::T(T&&);
T& operator=(T&&);
これらの操作には移動セマンティクスがあるため、渡された引数を自由に変更できます (オブジェクトを破壊可能な状態にしておく場合)。
本質的にはそれだけです。右辺値参照は、C++0x で完全な転送を可能にするためにも使用されることに注意してください (右辺値参照と他の型の間の特別に細工された型システムの相互作用のため)。ここです。
基本的に、右辺値参照を使用すると、オブジェクトが一時的なものであるかどうかを検出でき、オブジェクトの内部状態を保持する必要はありません。これにより、C ++ 03が常にコピーする必要があった、はるかに効率的なコードが可能になります。C++ 0xでは、同じリソースを再利用し続けることができます。さらに、右辺値参照により、完全な転送が可能になります。
この答えを見てください。
約 1 年間、大量のテキストの説明を読みましたが、Scott Meyer によるこの優れたプレゼンテーションを見るまで、r 値の参照に関するすべてを把握していませんでした: http://skillsmatter.com/podcast/home/move-semanticsperfect-forwarding- and-rvalue-references
彼は、プロセスで発生するすべてのことを理解するのに十分なほどゆっくりと面白い方法で説明します。
1 時間 30 分ですが、実際には、これが昨年の最高の説明です。
記事を読んだ後(他の回答と同様)、このビデオを見ると、一貫した方法で頭の中で一緒に溶け、数日後に何人かの同僚に説明し、std::unique_ptr の使用方法を説明することができました。関連しています-移動セマンティクスのみを許可し、コピーは許可しません)。これは、移動セマンティクスの理解が必要な std::move() の理解が必要なためです。
そのような質問を見てうれしく思います。私の意見を共有できてうれしいです。別の C++ 言語機能だけでなく、C++ 言語自体の指定に関するバグ修正について質問していると思います。「バグ」は何十年もそこにありました。つまり、コピー コンストラクターです。
コピー コンストラクターは、物理学でエネルギーや質量のようにコピーできないものがたくさんあることを知っていると、非常に奇妙に思えます。冗談ですが、実際にはプログラミングの世界でも、排他的なファイル記述子のようなオブジェクトはコピーできません。そのため、C++ のプログラマーとデザイナーは、それに対処するためのいくつかのトリックを発明しました。有名なものは NRVO と の 3 つboost::noncopyable
ですstd::auto_ptr
。
NRVO (Named Return Value Optimization) は、コピー コンストラクターを呼び出さずに、関数が値によってオブジェクトを返せるようにする手法です。しかし、NRVO の問題は、コピー コンストラクターが実際には呼び出されなくても、コピーpublic
コンストラクターの宣言がまだ必要であることです。つまり、のオブジェクトはboost::noncopyable
NRVO と互換性がありません。
std::auto_ptr
コピーコンストラクターをバイパスする別の試みです。その「コピー コンストラクター」が次のように実装されているのを見たことがあるかもしれません。
template <typename _T>
auto_ptr(auto_ptr<_T>& source)
{
_ptr = source._ptr; // where _ptr is the pointer to the contained object
source._ptr = NULL;
}
これはコピーではなく、「移動」です。この種の動作は、移動セマンティックのプロトタイプと見なすことができます。
ただしstd::auto_ptr
、独自の問題もあります。STL コンテナーと互換性がありません。したがって、残念なことに、コピー不可については何もかもが苦痛です。
C++0x のムーブ セマンティックが最終的に公開され、コンパイラ メーカーによって実装されるまで、これは苦痛でした。
簡単に言えば、移動セマンティックは の「コピー」動作と同じものと考えることができますがstd::auto_ptr
、言語機能によって完全にサポートされているため、コンテナーとアルゴリズムで正常に動作します。
ちなみに、C++0x ではstd::auto_ptr
は非推奨であり、新しいテンプレート タイプstd::unique_ptr
が推奨されています。
私の話はこれで終わります。奇妙な構文や右辺値システムなど、詳しく知りたい場合は他の投稿を参照してください。