問題タブ [rvo]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
c++ - C++: RVO、NRVO、およびローカル オブジェクトを返す
RVO (Return Value Optimization) と NRVO (Named Return Value Optimization) について読みました。以下に 2 つの例を示します
それは理にかなっており、優れたコンパイラの最適化です。ただし、Stanley Lippman の「C++ 入門書」から、「ローカル オブジェクトへの参照またはポインターを返さない」(ch 6.3.2) を読みました。コード例は次のとおりです。
わかりません。この例は RVO の例とどこか違うのでしょうか? それらが同じである場合、呼び出しスタックの巻き戻しによる未定義の動作を引き起こす代わりに、コンパイラーが RVO 最適化を行うようにするにはどうすればよいでしょうか?
c++ - 戻り値の最適化では、コピー コンストラクターを宣言する必要がありますか
Intel コンパイラが std::array オブジェクトの戻り値の最適化を生成しないことを発見しました。私のプログラムの内側のループにたまたまある次のコードは、可能な限り最適化されていません。
この動作は、標準ライブラリの実装が std::array のコピー コンストラクターを明示的に定義していないという事実に起因することがわかりました。次のコードは、次のことを示しています。
でコンパイルすると
レポートファイルが表示されます
これは、コンパイラが RVO を生成することを証明します (f の署名が変更され、新しく作成されたオブジェクトを呼び出し側のスタックに配置できるようになります)。ただし、 を宣言する行をコメント アウトするTest(const Test& x);
と、レポート ファイルに次のように表示されます。
これは、RVO が発生していないことを証明しています。
RVO を定義する C++11 標準の 12.8.31 では、彼らが提供する例にはコピー コンストラクターがあります。では、これは Intel コンパイラの「バグ」なのか、それとも標準に準拠した実装なのか?
c++ - 移動セマンティクスも RVO も期待どおりに機能しないのはなぜですか?
私は最近、方程式ソルバーで奇妙な動作に出くわしました。これにより、移動セマンティクスと RVO がどのように連携するかを本当に理解しているかどうか自問するようになりました。
このフォーラムには関連する質問がたくさんあり、これに関する一般的な説明もたくさん読みました。しかし、私の問題は非常に具体的であるように思われるので、誰かが私を助けてくれることを願っています.
関連する構造体は全体的に少し複雑ですが、少なくとも次のように分類されます。
次の短いプログラムを考えてみましょう。
これらは、このコードを実行する前の私の期待でした:
- この
Example
メソッドはローカルFoo
オブジェクトをインスタンス化し、デフォルトの ctorが呼び出されます。 Example
Foo
ローカルオブジェクトを値で返します。ただし、このコピーはRVOのために省略されると思います。- コピー ctorへの後続の呼び出しも最適化される可能性があります。代わり
a
に、 内の一時オブジェクトのアドレスが与えられる場合がありますExample
。 - 式を評価するために、左側のオペランドでメソッドが呼び出されます
a + a
。operator+
- 右側のオペランドは値で渡されるため、ローカル コピーを作成する必要がある場合があります。
- メソッド内では、参照渡し
operator+=
でそのコピーに対して呼び出されます。*this
- ここで、呼び出し元のメソッド
operator+=
の return ステートメントにジャンプして、再び同じローカル コピーへの参照を返します。operator+
- 参照されたオブジェクトは、最終的に値によって返されます。ここでは、ローカル コピーの値を保持する必要があるだけなので
b
(以前のステップ 2 と 3 で発生したように)、別のコピー省略が予想されます。 - オブジェクト
a
とオブジェクトの両方b
が最終的にスコープ外になるため、それらのデストラクタを呼び出します。
(少なくとも私にとって) 驚くべき観察結果は、ステップ 8 でディープ コピーが最適化されていないことです (使用されたコンパイラ オプションに関係なく)。代わりに、出力は次のようになります。
の次の小さな変更は、operator+
まったく違いがないように見えました。
ただし、今回の結果は完全に異なります。
明らかに、コンパイラはxvaluerhs
として認識され(明示的に を記述した場合と同様に)、代わりにmove ctorを呼び出します。return move(rhs += *this);
さらに、-fno-elide-constructors
オプションを使用すると、常に次のようになります。
私が信じていることから、コンパイラは行かなければなりません
- RVO (可能であれば)、または
- 建設を移動する(可能であれば)、または
- コピー構築(そうでなければ)、
その順序で。だから私の質問は: 誰かが私に説明してもらえますか、ステップ 8 で実際に何が起こるのか、なぜ上記の優先順位の規則が適用されないのか (または、もしそうなら、ここに表示されていないものは何ですか)? 冗長な例で申し訳ありませんが、事前に感謝します。
-std=c++11
現在、最適化をオフにしてgcc mingw-w64 x86-64 v.4.9.2を使用しています。
ps - 適切な OO コードの書き方とカプセル化の方法について私にアドバイスしたいという衝動を抑えてください ;-)
c++ - スタック割り当てオブジェクトへのポインタとムーブ構造
注:これは、私がしばらく前に投稿した質問の完全な言い回しです。重複している場合は、もう一方を閉じてください。
私の問題は非常に一般的ですが、具体的な簡単な例に基づいて、より簡単に説明できるようです。オフィスの電力消費量をシミュレートしたいとします。照明と暖房しかないと仮定しましょう。
重要な点は次のとおりです。
1.Time
データ メンバーに移動できない、Light
またはHeating
その変更がこれらのクラスのいずれにも由来しないため。
2.Time
にパラメーターとして明示的に渡す必要はありませんLight
。実際、Light
プログラムのどの部分にも、パラメーターとして提供したくない への参照が存在する可能性がTime
あります。
これで、Simulation
コピー/移動が構築されていない限り、完全に見つかります。そうである場合は、コピー/移動も構築され、デフォルトで、Time へのポインターはコピー/移動元の古いインスタンスをLight
指しているからです。ただし、実際には、return ステートメントとオブジェクト作成の間で copy/move が構築されます。Time
Simulation
Simulation
SimulationBuilder::build()
main()
現在、問題を解決する方法はいくつかあります。
1: コピー省略に依存します。この場合(そして私の実際のコードでは)、コピーの省略は標準で許可されているようです。しかし必須ではなく、実際のところ、clang -O3 によって省略されることはありません。より正確に言うと、clang はSimulation
コピーを省略しますが、move ctor for を呼び出しますLight
。また、実装依存の時間に依存することは堅牢ではないことに注意してください。
2: で move-ctor を定義しSimulation
ます。
これは機能しますが、ここでの大きな問題はカプセル化を弱めることです:移動中により多くの情報が必要でSimulation
あることを知る必要があります。Light
この単純化された例では、これはそれほど悪くはありませんが、timePtr
直接ではなくLight
、そのサブサブサブメンバーの 1 つにあると想像してください。それから私は書かなければならないでしょう
これは、カプセル化とデメテルの法則を完全に破ります。関数を委任するときでさえ、私はそれが恐ろしいと思います。
Time
3: によって監視されているある種のオブザーバー パターンを使用し、メッセージを受信するときにポインターを変更するLight
ように、コピー/移動が構築されているときにメッセージを送信します。Light
私はそれの完全な例を書くのが面倒だと告白しなければなりません。
4: で所有ポインタを使用しますSimulation
。
が移動Simulation
すると、Time
メモリは ではないため、 のポインタLight
は無効になりません。実際、これは他のほとんどすべてのオブジェクト指向言語が行うことです。すべてのオブジェクトはヒープ上に作成されるからです。今のところ、私はこの解決策を支持していますが、まだ完全ではないと考えています。B. Stroustrup が、必要のないときはポインタを使用すべきではなく、必要な場合は多かれ少なかれポリモーフィックであることを意味すると言っているのを聞いたことがあります。
5:Simulation
返されることなく、その場で構築しSimulationBuilder
ます (その後、コピー/移動 ctor/代入をSimulation
すべて削除できます)。例えば
今、私の質問は次のとおりです。
1: どのようなソリューションを使用しますか? もう一つ考えてみませんか?
2: 元のデザインに何か問題があると思いますか? それを修正するためにあなたは何をしますか?
3: このようなパターンに遭遇したことがありますか? 私のコード全体でかなり一般的です。Time
ただし、実際にはポリモーフィックであり、ヒープが割り当てられているため、一般的にはこれは問題ではありません。
4: 問題の根本に戻ると、「移動する必要はありません。移動できないオブジェクトをその場で作成したいだけですが、コンパイラはそうすることができません」なぜないのですか? C ++での簡単な解決策と、別の言語での解決策はありますか?
c++ - 戻り値の最適化が確実に行われるようにするにはどうすればよいですか
値によって巨大なオブジェクトを返す関数を作成しました。私の同僚は、冗長なコピーを行い、関数の引数として参照によってオブジェクトを返すことを提案すると不満を漏らしています。戻り値の最適化が行われ、コピーが削除されることはわかっていますが、コードはさまざまなコンパイラでコンパイルできるライブラリで使用されるため、それらすべてをテストすることはできません。値によってオブジェクトを返すことが保存されていることを同僚に納得させるために、それが述べられているドキュメントが必要です。
私は c++03 標準を見てきましたが、戻り値の最適化については何も見つかりません。RVOが行われることが定義されているドキュメント(標準)へのリンクを教えてください。または、RVO をサポートするコンパイラのリストを見つけることができる場所が存在しない場合は?