1. ホーム
  2. c++

[解決済み] 関数の戻り値に対して std::move を使用するのはいつですか?重複

2022-05-10 17:29:42

質問

この場合

struct Foo {};
Foo meh() {
  return std::move(Foo());
}

移動が不要なのは確かで、新しく作られた Foo はxvalueになるからです。

しかし、このような場合はどうでしょうか?

struct Foo {};
Foo meh() {
  Foo foo;
  //do something, but knowing that foo can safely be disposed of
  //but does the compiler necessarily know it?
  //we may have references/pointers to foo. how could the compiler know?
  return std::move(foo); //so here the move is needed, right?
}

そこに移動が必要なのでしょう?

どのように解決するのですか?

の場合 return std::move(foo);move は12.8/32のため不要です。

コピー操作のエリシオンの基準が満たされている、または満たされているであろうとき コピー元のオブジェクトが関数のパラメータであるという事実を除けば、コピー操作のエリジオンの基準が満たされているか、または満たされている場合。 コピーされるオブジェクトがl値で指定されている場合、コピー用のコンストラクタを選択するためにオーバーロード コピーするコンストラクタを選択するためのオーバーロードの解決は、オブジェクトが rvalue で指定されている場合と同様に最初に実行されます。 オブジェクトがr値で指定されている場合と同じです。

return foo; はNRVOの場合なので、コピーエリジョンが許可されています。 foo は lvalue です。ですから、コンストラクタで選択された "copy" から foo の戻り値への meh は、もし存在するならば、移動コンストラクタであることが要求されます。

追加する move を追加することは、潜在的な効果がありますが、それは、移動がエリッドされるのを防ぐことです。 return std::move(foo); ではない NRVOの対象外です。

私の知る限り、12.8/32は、以下のように定めています。 だけ を規定しています。コンパイラは一般に、コピー後に lvalue が未使用であることを検出し (例えば DFA を使用して)、自分自身の判断で変更を加えることは許可されていません。私はここで、この 2 つの間に観察可能な違いがあると仮定しています。観察可能な動作が同じであれば、"as-if" 規則が適用されます。

というわけで、タイトルの質問に答えるために std::move を返り値に使うと、移動させたいときに、どうせ移動されないだろうから。ということです。

  • 移動してほしいとき、そして
  • はlvalueであり
  • はコピー消去の対象外であり
  • 値による関数パラメータの名前ではありません。

これを考慮すると、かなり手間がかかり、移動は 通常 であることを考えると、テンプレート以外のコードでは、これを少し単純化することができると言うのがいいかもしれません。使用方法 std::move のときに

  • 移動させたい
  • はlvalueであり
  • であり、それを気にすることはできません。

簡略化されたルールに従うことで、いくつかの手の消去を犠牲にすることになります。以下のようなタイプでは std::vector のような、移動が安価なタイプでは、おそらく気づくことはないでしょう (そして気づいたとしても最適化することができます)。のようなタイプでは std::array のようなタイプ、または移動が安価であるかどうか見当もつかないテンプレートでは、それを気にする可能性が高くなります。