147

この場合

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

そこに移動が必要だと思いますか?

4

6 に答える 6

151

12.8/32 のために が余分なreturn std::move(foo);場合:move

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

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

ただし、追加には潜在的な効果があります。NRVO の資格がないmoveため、移動が省略されるのを防ぎます。return std::move(foo);

私の知る限り、12.8/32 は、左辺値からのコピーを移動に置き換えることができる唯一の条件を示しています。コンパイラーは、一般に、コピー後に左辺値が使用されていないことを検出し (たとえば、DFA を使用して)、独自のイニシアチブで変更を行うことは許可されていません。ここでは、この 2 つの間に観察可能な違いがあると想定しています。観察可能な動作が同じである場合は、"as-if" ルールが適用されます。

したがって、タイトルの質問に答えるには、std::move移動させたいときに戻り値を使用しますが、とにかく移動しません。あれは:

  • あなたはそれを動かしたい、そして
  • それは左辺値であり、
  • コピー省略の対象外であり、かつ
  • 値渡しの関数パラメーターの名前ではありません。

これは非常に手間がかかり、移動は通常安価であることを考えると、非テンプレート コードではこれを少し単純化できると言うことができます。次の場合に使用std::moveします。

  • あなたはそれを動かしたい、そして
  • それは左辺値であり、
  • 気にしても仕方ありません。

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

于 2013-02-13T15:07:24.790 に答える
41

どちらの場合も移動は不要です。2 番目のケースでstd::moveは、値によってローカル変数を返すため不要であり、コンパイラは、そのローカル変数をもう使用しないため、コピーするのではなく移動できることを理解します。

于 2013-02-13T14:58:38.517 に答える
30

戻り値で、戻り式がローカル左辺値 (つまり、この時点では xvalue) の名前を直接参照する場合、. は必要ありませんstd::move。一方、戻り式が識別子でない場合は、自動的に移動されないため、たとえば、std::moveこの場合は明示的にする必要があります。

T foo(bool which) {
   T a = ..., b = ...;
   return std::move(which? a : b);
   // alternatively: return which? std::move(a), std::move(b);
}

名前付きローカル変数または一時式を直接返すときは、明示的なstd::move. そのような場合、コンパイラは自動的に移動する必要があり(将来的には移動する予定です)、追加するとstd::move他の最適化に影響を与える可能性があります。

于 2013-02-13T15:39:29.730 に答える
23

移動してはいけない場合については多くの回答がありますが、問題は「いつ移動する必要があるか」です。

これを使用する必要がある場合の不自然な例を次に示します。

std::vector<int> append(std::vector<int>&& v, int x) {
  v.push_back(x);
  return std::move(v);
}

つまり、右辺値参照を受け取り、それを変更し、そのコピーを返す関数がある場合です。( 、ここでの動作が変わります)実際には、この設計はほとんど常に優れています。

std::vector<int> append(std::vector<int> v, int x) {
  v.push_back(x);
  return v;
}

これにより、右辺値以外のパラメーターを使用することもできます。

基本的に、移動して返したい関数内に右辺値参照がある場合は、 を呼び出す必要がありますstd::move。ローカル変数がある場合 (パラメーターであろうとなかろうと)、それを暗黙的に返しますmove(この暗黙的な移動は省略できますが、明示的な移動はできません)。ローカル変数を取り、そのローカル変数への参照を返す関数または操作がある場合は、std::movemove を発生させる必要があります (例として、三?:項演算子)。

于 2013-02-13T15:36:36.520 に答える
-8

std::move関数から戻るときはまったく不要であり、実際にはプログラマーの領域に入り、コンパイラーに任せるべきことを子守しようとします。

std::move関数のローカル変数ではない関数から何かを取得するとどうなりますか? そのようなコードを書くことは決してないと言うことができますが、問題のないコードを記述し、それをリファクタリングしてぼんやりと変更しないとどうなりますかstd::move. そのバグを追跡するのは楽しいでしょう。

一方、コンパイラは、ほとんどの場合、この種の間違いを犯すことができません。

また、関数からローカル変数を返すことは、必ずしも右辺値を作成したり、移動セマンティクスを使用したりするとは限らないことに注意してください

こちらをご覧ください。

于 2013-02-13T15:01:52.120 に答える