2011-06-17 3 views
10

は、私は次の最適化が期待どおりに動作かどうかを確認したかった:コピーエリートの条件は?

  • RVO RVO名前

は、だから私はこの小さなプログラムを書いた値で引数を渡すときに

  • コピーの省略:

    #include <algorithm> 
    #include <cstddef> 
    #include <iostream> 
    #include <vector> 
    
    struct Foo { 
        Foo(std::size_t length, char value) : data(length, value) { } 
    
        Foo(const Foo & rhs) : data(rhs.data) { std::cout << "*** COPY ***" << std::endl; } 
    
        Foo & operator= (Foo rhs) { 
         std::cout << "*** ASSIGNMENT ***" << std::endl; 
         std::swap(data, rhs.data); // probably expensive, ignore this please 
         return *this; 
        } 
    
        ~Foo() { } 
    
        std::vector<char> data; 
    }; 
    
    Foo TestRVO() { return Foo(512, 'r'); } 
    
    Foo TestNamedRVO() { Foo result(512, 'n'); return result; } 
    
    void PassByValue(Foo inFoo) {} 
    
    int main() 
    { 
        std::cout << "\nTest RVO: " << std::endl; 
        Foo rvo = TestRVO(); 
    
        std::cout << "\nTest named RVO: " << std::endl; 
        Foo named_rvo = TestNamedRVO(); 
    
        std::cout << "\nTest PassByValue: " << std::endl; 
        Foo foo(512, 'a'); 
        PassByValue(foo); 
    
        std::cout << "\nTest assignment: " << std::endl; 
        Foo f(512, 'f'); 
        Foo g(512, 'g'); 
        f = g; 
    } 
    

    そして、最適化enエーブル:

    Test RVO: 
    
    Test named RVO: 
    
    Test PassByValue: 
    *** COPY *** 
    
    Test assignment: 
    *** COPY *** 
    *** ASSIGNMENT *** 
    

    出力RVOと予想通りという名前RVO作業によると:

    $ g++ -o test -O3 main.cpp ; ./test 
    

    これが出力されます。ただし、代入演算子とPassByValueを呼び出す場合は、コピーエリートは実行されません。

    ユーザ定義のコピーコンストラクタでcopy elisionを使用できませんか? (私は、RVOが標準で明示的に許可されていることを知っていますが、値渡しの際にはコピーエリジョンについて知らないのです)コピーコンストラクタを定義せずにコピーエリッションを検証する方法はありますか?

  • +1

    (N)RVO *は*コピーエリートです。それらは唯一の形式ではありませんが、あなたの例では、コピーエリジョンが実行されていないことが不正確であることを示しています。 –

    +0

    Cooy elisionは、一般にすべての一時的なオブジェクトに対して許可されますが、名前付きオブジェクトまたは参照対象オブジェクトには許可されません。 gccは許可されたものを正確に実行するようです。 –

    +0

    @Dennis Zickefooseありがとう、私はテキストを修正しました。 – StackedCrooked

    答えて

    9

    コピーコンストラクタを使用する方法は、コピーしたオブジェクトがコール後も存在するため、削除できません。

    あなたはこのようにそれをしようとすると、それは良い仕事かもしれません:

    PassByValue(Foo(512, 'a')); 
    

    すべての最適化が可能ですが、必須ではありませんので、それはそれはして行いますができるかを決定するために、各コンパイラに任されています。

    +0

    ここで実際に最適化が行われます。この例は@ Space_C0wb0yの答えと共に私にそれを得させます。 – StackedCrooked

    10

    標準は(段落12.8.15に)言う:

    コピー操作のこのエリジオンは (複数のコピーを排除する を組み合わせることができる)以下 状況で許可されている:

    • return文で、 クラス戻り型の場合、式 が不揮発性の名前の場合機能 戻り型と同じ CV-非修飾型の自動オブジェクト、コピー操作は、関数の 戻り値に直接

    • とき一時クラス オブジェクトことを自動 オブジェクトを構築することによって を省略することができます 参照(12.2)に結合されていない直接のターゲットにtempo- RARYオブジェクト を構築することにより、コピー 操作を省略することができ、同じ CV-非修飾型で クラスオブジェクトにコピーされます 省略コピー

    これらの例はいずれも、ここで適用されるので、エリジオンは許可されていません。最初のことは明らかです(返りません)。 2番目は許可されません。渡すオブジェクトは一時的なものではないからです。

    とにかくコピーを作成する必要があるため、コードはまだ問題ありません。そのコピーを取り除くには、C++ 0xの移動セマンティクスを使用する必要があります。

    +1

    C++ 11では、セクション番号が§12.8/ 31になり、コピー/移動のエリッションが許可される2つの状況がありますが、例外処理のみに関連しています。 – kennytm