3

元のコード

#include <iostream>
int global;
struct A
{
   A(){}
   A(const A&x){
       ++global;
   }
   ~A(){}
};
A foo()
{  
     A a;
     return a;  
}
int main()
{
   A x = foo();
   std::cout << global;
}

出力は0、名前付き戻り値の最適化をサポートする最適化されたコンパイラで行われます。

の定義を変更するfoo

A foo()
{ 
  { 
     A a;
     return a;  
  }
}

出力として取得1します。つまり、コピー c-tor が 1 回呼び出されます。考えられる理由は何ですか?ダミー スコープを導入すると、コードの動作が完全に変わります。私は何が欠けていますか?

g++ コンパイラでテストしました。実装固有の方法でシナリオを説明できるコンパイラ担当者はいますか?

編集

clang でテストしたところ、2 番目のケースでもコピー c-tor の呼び出しが最適化されました。

Andrew Pinski (gcc 担当者) は、これが実際に g++ の最適化を逃したケースであることを確認しました。

4

5 に答える 5

3

少なくともこの特定のコードでは、ダミーのスコープ (余分なブラケットによって導入された) がまったく違いがないことを確認するのに十分なほどコンパイラが賢くないという以外の理由はわかりません。コンパイラは余分なブラケットにだまされています。おそらく、関数本体の残りの部分(存在すらしていない)について、ワイルドな仮定を行ったのでしょう。

ゼロまたは 1 のいずれかの方法で、動作は完全に標準に準拠しています。標準では、コンパイラが生成する必要0がないため(または1それについて)。ご存知のように、それはコンパイラ次第です。

どちらの場合も生成されたアセンブリ コードには、fooわずかな違いが 1 つあります。

  • 最初のコード:

    __Z3foov:
    LFB992:
         .cfi_startproc
         movl  4(%esp), %eax
         ret   $4
         .cfi_endproc
    
  • 2 番目のコード:

    __Z3foov:
    LFB992:
         .cfi_startproc
         incl _global      <----- incrementing the global. God knows why!
         movl  4(%esp), %eax
         ret   $4
         .cfi_endproc
    

使用しg++ -O6ました。バージョン :MinGW (GCC) 4.6.1

于 2011-12-15T15:51:38.087 に答える
2

理論的には、両方の状況で 1 を取得できます。これは、コンパイラがコピー コンストラクターを最適化する場合としない場合がある状況の 1 つです。

「戻り値の最適化」「名前付き戻り値の最適化」をグーグルで検索すると、この件に関するより詳細な情報を見つけることができます。あなたの場合は後者です。

コードを次のように変更する場合は注意してください。

A foo()
{ 
  { 
     return A();
  }
}

その後、RVO が起動し、出力で 0 が得られます。

あなたが説明したケースで NRVO が介入しなかったのはなぜですか? (これは GCC 4.6 で確認済みです。)この時点ではよくわかりません。コンパイラが十分に賢くないか、ここでそれを許可しない NRVO に関するルールがあります。


編集:

基準はというと…

特定の基準が満たされると、オブジェクトのコピー/移動コンストラクターおよび/またはデストラクタに副作用がある場合でも、実装はクラス オブジェクトのコピー/移動構築を省略できます。(...) コピー省略と呼ばれるこのコピー/移動操作の省略は、次の状況で許可されます。

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

したがって、ここでは許可されていますが、コンパイラはここで NRVO を実行するほど賢くありませんでした。GCC で作業している場合は、Clang でも同じかどうかを確認し、結果が異なるかどうかを確認できます (私の直感ではそうなるでしょう)。

RVO と NRVO はコンパイラ機能の名前であることに注意してください。一方、"copy elision" は、標準がこの動作を一般的に参照する方法です。

于 2011-12-15T15:38:40.927 に答える
2

どのコンパイラで?どちらの結果も合法であるため、正式には、文句を言うことはできません。実際には、スコープの導入によって何かが変わる理由がわかりません。実装の品質の問題として、文句を言うことができると思います。

于 2011-12-15T15:48:16.410 に答える
0

コンパイラが場合によっては最適化を見逃す可能性があるため、他の場合でも最適化は可能です。私の知る限り-コピーコンストラクターの省略を許可する場所を確認しました-変数が関数の最上位スコープにある必要があるという制約はありません。それは単に自動変数でなければなりません。

于 2011-12-15T15:40:07.180 に答える