web-dev-qa-db-ja.com

C ++での移動セマンティクス-ローカル変数のMove-Return

私の理解では、C++ 11では、関数からローカル変数を値で返す場合、コンパイラーはその変数をr値参照として扱い、関数から「移動」して返すことができます(もちろん、代わりにRVO/NRVOは発生しません)。

私の質問は、これは既存のコードを壊すことができないのですか?

次のコードを検討してください。

_#include <iostream>
#include <string>

struct bar
{
  bar(const std::string& str) : _str(str) {}
  bar(const bar&) = delete;
  bar(bar&& other) : _str(std::move(other._str)) {other._str = "Stolen";}
  void print() {std::cout << _str << std::endl;}

  std::string _str;
};

struct foo
{
  foo(bar& b) : _b(b) {}
  ~foo() {_b.print();}

  bar& _b;
};

bar foobar()
{
  bar b("Hello, World!");
  foo f(b);

  return std::move(b);
}

int main()
{
  foobar();
  return EXIT_SUCCESS;
}
_

私の考えでは、ローカルオブジェクトのデストラクタが暗黙的に移動されたオブジェクトを参照する可能性があるため、予期せず「空の」オブジェクトが表示されると考えていました。私はこれをテストしようとしましたが( http://ideone.com/ZURoeT を参照)、foobar()に明示的な_std::move_を指定せずに「正しい」結果を得ました。これはNRVOが原因だったと思いますが、コードを再配置して無効にすることはしませんでした。

この変換(関数の外への移動を引き起こす)は暗黙的に発生し、既存のコードを壊す可能性があるという点で私は正しいのですか?

[〜#〜] update [〜#〜]以下は、私が話していることを示す例です。次の2つのリンクは同じコード用です。 http://ideone.com/4GFIR -C++ 03 http://ideone.com/FcL2Xj -C++ 11

出力を見ると異なります。

それで、この質問が今になると思います、これは標準に暗黙の移動を追加するときに考慮されましたか?この種類のコードは十分にまれなので、この重大な変更を追加しても問題ないことが決定されましたまた、このような場合にコンパイラが警告するかどうかも疑問に思います...

10
Bwmat

Scott Meyers posted to comp.lang.c ++(August 2010)moveコンストラクタの暗黙的な生成がC++ 03クラスの不変式を壊す可能性がある問題について:

_struct X
{
  // invariant: v.size() == 5
  X() : v(5) {}

  ~X() { std::cout << v[0] << std::endl; }

private:    
  std::vector<int> v;
};

int main()
{
    std::vector<X> y;
    y.Push_back(X()); // X() rvalue: copied in C++03, moved in C++0x
}
_

ここでの問題は、C++ 03では、Xに、vメンバーが常に5つの要素を持つという不変式があったことです。 X::~X()はその不変式に基づいていますが、新しく導入されたmoveコンストラクターはvから移動し、その長さをゼロに設定しました。

壊れた不変条件はXのデストラクタでのみ検出されるため、これはあなたの例に関連しています(ローカルオブジェクトのデストラクタが暗黙的に移動されたオブジェクトを参照し、したがって予期せずemptyobject)。

C++ 11は、既存のコードの一部を壊すことと、移動コンストラクターに基づいて有用な最適化を提供することの間のバランスをとろうとします。

委員会は最初、移動コンストラクターと移動割り当て演算子は、ユーザーによって提供されない場合はコンパイラーによって生成されるべきであると決定しました。

次に、これは確かに警告の原因であると判断し、既存のコード(たとえば、明示的に定義されたデストラクタ)が壊れる可能性は非常に低くなるような方法で、移動コンストラクタと移動割り当て演算子の自動生成を制限しました。

ユーザー定義のデストラクタが存在する場合に暗黙のmoveコンストラクタの生成を防止することで十分であると考えるのは魅力的ですが、それは真実ではありません( N3153-Implicit Move Must Go for more details)。

N3174-移動するかしないか Stroupstrupは言う:

これは、単純な下位互換性の問題ではなく、言語設計の問題と考えています。古いコードを壊すことは簡単に回避できます(たとえば、C++ 0xから移動操作を削除するだけ)。ただし、C++ 0xをより優れた言語にするには、移動操作を普及させることをC +を壊す価値のある主要な目標にすることです。 +98コード。

8
manlio