受け入れられた答えは良い答えです(そして私はそれを賛成しました)。しかし、私はこの質問にもう少し詳しく対処したいと思いました。
私の質問の核心は、なぜムーブ代入演算子を自動的に選択しないのかということです。コンパイラは、割り当て後にvが使用されていないことを知っていますね。それとも、C ++ 11はコンパイラがそれほどスマートである必要はありませんか?
この可能性は、移動セマンティクスの設計中に検討されました。極端な場合、コンパイラーに静的分析を実行させ、可能な場合はいつでもオブジェクトから移動させたい場合があります。
void set_name(std::string v)
{
name_ = v; // move from v if it can be proven that some_event is false?
if (some_event)
f(v);
}
最終的に、コンパイラにこの種の分析を要求することは非常に注意が必要です。一部のコンパイラは証明を作成できる場合とできない場合があります。したがって、実際には移植性のないコードにつながります。
では、ifステートメントのない単純なケースについてはどうでしょうか。
void foo()
{
X x;
Y y(x);
X x2 = x; // last use? move?
}
y.~Y()
さて、気づきx
がから移動したかどうかを知るのは難しいです。そして一般的に:
void foo()
{
X x;
// ...
// lots of code here
// ...
X x2 = x; // last use? move?
}
x
コンパイラがこれを分析して、へのコピー構築後に本当に使用されなくなったかどうかを知ることは困難ですx2
。
したがって、元の「移動」提案は、非常に単純で非常に保守的な暗黙の移動のルールを提供しました。
左辺値は、コピーの省略がすでに許可されている場合にのみ暗黙的に移動できます。
例えば:
#include <cassert>
struct X
{
int i_;
X() : i_(1) {}
~X() {i_ = 0;}
};
struct Y
{
X* x_;
Y() : x_(0) {}
~Y() {assert(x_ != 0); assert(x_->i_ != 0);}
};
X foo(bool some_test)
{
Y y;
X x;
if (some_test)
{
X x2;
return x2;
}
y.x_ = &x;
return x;
}
int main()
{
X x = foo(false);
}
ここで、C ++ 98/03の規則により、このプログラムは、コピーの省略が発生するかどうかに応じて、アサートする場合としない場合return x
があります。それが発生した場合、プログラムは正常に実行されます。それが起こらない場合、プログラムはアサートします。
そしてそれは推論されました:RVOが許可されるとき、私たちはすでにの値に関して保証がない領域にいますx
。したがって、この余裕を利用してから移動できるはずx
です。リスクは小さく見え、利益は大きく見えました。これは、多くの既存のプログラムが単純な再コンパイルではるかに高速になることを意味するだけでなく、ファクトリ関数から「移動のみ」の型を返すことができることも意味します。これは、リスクに対する非常に大きなメリットです。
標準化プロセスの後半で、私たちは少し貪欲になり、値によるパラメーターを返すときに暗黙の移動が発生する(そして型が戻り型と一致する)とも言いました。ここでもメリットは比較的大きいように見えますが、RVOが合法であった(または合法である)場合ではないため、コードが破損する可能性はわずかに高くなります。しかし、この場合のコードを壊すデモはありません。
したがって、最終的には、コアの質問に対する答えは、移動セマンティクスの元の設計が、既存のコードの解読に関して非常に保守的なルートをとったということです。もしそうでなかったら、それは確かに委員会で撃墜されたでしょう。プロセスの後半に、デザインを少しアグレッシブにするいくつかの変更がありました。しかし、この時までに、核となる提案は、大多数の(しかし全会一致ではない)支持を得て、標準にしっかりと定着していました。