7

適度に優れた C++ 11 コンパイラ (clang) がこのコードを最適化しない理由を説明しようとしています。

#include <iostream>
#define SLOW

struct A {
  A() {}
  ~A() { std::cout << "A d'tor\n"; }
  A(const A&) { std::cout << "A copy\n"; }
  A(A&&) { std::cout << "A move\n"; }
  A &operator =(A) { std::cout << "A copy assignment\n"; return *this; }
};

struct B {
  // Using move on a sink. 
  // Nice talk at Going Native 2013 by Sean Parent.
  B(A foo) : a_(std::move(foo)) {}  
  A a_;
};

A MakeA() {
  return A();
}

B MakeB() {  
 // The key bits are in here
#ifdef SLOW
  A a(MakeA());
  return B(a);
#else
  return B(MakeA());
#endif
}

int main() {
  std::cout << "Hello World!\n";
  B obj = MakeB();
  std::cout << &obj << "\n";
  return 0;
}

これを#define SLOWコメントアウトして最適化して実行すると-s

Hello World!
A move
A d'tor
0x7fff5fbff9f0
A d'tor

これは予想されます。

これを#define SLOW有効にして最適化して実行すると、次の-sようになります。

Hello World!
A copy
A move
A d'tor
A d'tor
0x7fff5fbff9e8
A d'tor

これは明らかにそれほど良くありません。質問は次のとおりです。

「SLOW」の場合に適用された NRVO 最適化が表示されないのはなぜですか? コンパイラが NRVO を適用する必要がないことは知っていますが、これは非常に一般的な単純なケースのようです。

一般的に、私は「SLOW」スタイルのコードを推奨するようにしています。これは、デバッグがはるかに簡単だからです。

4

2 に答える 2

13

簡単な答えは次のとおりです。この場合、コピー省略を適用することは許可されていないためです。コンパイラは、非常に少数の特定のケースでのみコピー省略を適用できます。標準からの引用は、12.8 [class.copy] パラグラフ 31 です。

... コピー省略と呼ばれるこのコピー/移動操作の省略は、次の状況で許可されます (複数のコピーを排除するために組み合わせることができます)。

  • クラスの戻り値の型を持つ関数の return ステートメントで、式が関数の戻り値の型と同じ cv 非修飾型を持つ不揮発性自動オブジェクト (関数または catch-clause パラメーター以外) の名前である場合、自動オブジェクトを関数の戻り値に直接構築することにより、コピー/移動操作を省略できます
  • [...]

の型がB(a)is notAであることは明らかです。つまり、コピーの省略は許可されていません。同じ段落の他の箇条書きは、throw式、一時的なコピーの削除、例外宣言などを参照しています。これらのどれも当てはまりません。

于 2013-12-18T05:17:19.790 に答える
3

スロー パスで見られるコピーは、RVO の不足が原因ではなく、B(MakeA()) では "MakeA()" が右辺値ですが、B(a) では "a" が右辺値であるという事実が原因です。左辺値。

これを明確にするために、低速パスを変更して MakeA() が完了した場所を示しましょう。

#ifdef SLOW
  A a(MakeA());
  std::cout << "---- after call \n";
  return B(a);
#else

出力は次のとおりです。

Hello World!
---- after call 
A copy
A move
A d'tor
A d'tor
0x7fff5a831b28
A d'tor

これは、コピーが行われなかったことを示しています

A a(MakeA());

したがって、RVOが発生しました。

すべてのコピーを削除する修正は次のとおりです。

return B(std::move(a));
于 2014-01-05T23:45:23.847 に答える