関数の戻り値に std::move を使用するのはいつですか? [重複] 質問する

関数の戻り値に std::move を使用するのはいつですか? [重複] 質問する

この場合

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?
}

そこで移転が必要だと思いますか?

ベストアンサー1

return std::move(foo);の場合、move12.8/32 のため不要です。

コピー操作の省略の基準が満たされているか、またはソース オブジェクトが関数パラメータであり、コピーされるオブジェクトが lvalue によって指定されているという事実を除いて満たされる場合、オブジェクトが rvalue によって指定されているかのように、コピーのコンストラクターを選択するためのオーバーロード解決が最初に実行されます。

return foo;は NRVO のケースなので、コピー省略が許可されます。は左辺値です。したがって、 から の戻り値へfooの「コピー」に選択されたコンストラクタは、存在する場合は移動コンストラクタである必要があります。foomeh

しかし、追加するmoveと潜在的な効果があります。それは、移動が省略されるのを防ぐためreturn std::move(foo);です。ないNRVOの対象となります。

私の知る限り、12.8/32はのみlvalue からのコピーを移動によって置き換えることができる条件。コンパイラは一般に、コピー後に lvalue が未使用であることを検出 (たとえば DFA を使用) して、独自の判断で変更を加えることはできません。ここでは、2 つの間に目に見える違いがあると想定しています。目に見える動作が同じであれば、「as-if」ルールが適用されます。

したがって、タイトルの質問に答えるには、std::move戻り値を移動したいが、移動されない場合は、戻り値に を使用します。つまり、次のようになります。

  • 移動させたい場合、
  • それは左辺値であり、
  • コピー省略の対象にはならず、
  • 値渡し関数パラメータの名前ではありません。

これはかなり面倒で、動きがいつもの安価ですが、テンプレート以外のコードではこれを少し簡略化できると言うかもしれません。次のstd::move場合に使用します。

  • 移動させたい場合、
  • それは左辺値であり、
  • それについて心配する必要はありません。

簡略化されたルールに従うと、移動省略がいくらか犠牲になります。std::vector移動コストが安いタイプの場合、おそらく気付かないでしょう (気付いたとしても最適化できます)。std::array移動コストが高いタイプの場合、または移動コストが安いかどうかわからないテンプレートの場合、そのことを気にする方が面倒です。

おすすめ記事