2

私は NRVO を調査しており、さまざまなコンパイラでサポートされていますが、奇妙な動作に遭遇しました。これはかなり混乱しています。

サンプルコード:

    #include <iostream>
    using namespace std;

    class X {
        public:
            X() {
                cout << "Constructor 1" << endl;
            }   
    };

    X test() {
        X a;
        cout << &a << endl;
        return a;
    }

    int main() {
        X b = test();
        cout << &b << endl;
        return 0;
    }

最適化レベル 2 (または 3) でコンパイルすると、出力は 2 つの異なるメモリ アドレスになります。ただし、私が理解している限り、関数コードは NRVO に対して有効です。

PS VS2010 でコンパイルされた同じコードと最適化レベル 2 は NRVO を使用します。

追加のコンストラクターを追加すると:

X(const X& h) { 
    cout << "Contsructor 2" << endl;        
}

コンパイル後のアドレスは同じなので、NRVO が適用されていると思いますが、最適化レベルに関係なく発生します。

" X(const X& h) " は何らかの形で g++ に NRVO を使用することを暗示していますか? それともNRVOが全く適用されておらず何か違うのでしょうか?

前もって感謝します。

追加した。GCC バージョン:

$ g++ -v

Configured with: [....] -enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --program-suffix=-4.4 --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-plugin --enable-objc-gc --disable-werror --with-arch-32=i486 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu

gcc version 4.4.3

追加のテスト:X(const X& h)コンストラクトと-fno-elide-constructorsg++ フラグを使用してコンパイルすると、「コピー」コンストラクターが呼び出され、予期される動作が行われます。それがなければ、NRVO は失敗します。異なるマシンでテスト: gcc 4.4.3 と gcc 4.3.* -> 同じ結果。

これまでのところ、__cxa_atexit (@yves の g++ のバージョンに存在) が何らかの形で異なる動作に影響を与えるかどうかはわかりません。(私が理解している限り、それとは関係ありませんが、それが呼び出し順序にもたらす変化を完全には理解していません)

4

2 に答える 2

1

私にとっては問題ありません。最適化を有効にしていなくても、nrvo が適用されます。

$ mk nrvo
g++     nrvo.cpp   -o nrvo
$ ./nrvo
Constructor 1
0xbfe5a520
0xbfe5a520
$ g++ -v
Lecture des spécification à partir de /usr/lib/gcc/i386-redhat-linux/3.4.6/specs
Configuré avec: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --disable-checking --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-java-awt=gtk --host=i386-redhat-linux
Modèle de thread: posix
version gcc 3.4.6 20060404 (Red Hat 3.4.6-8)

EDIT :完全に最適化してコンパイルしてみてください:

g++ -O3 nrvo.cpp   -o nrvo

それが役に立てば幸い。

于 2012-02-10T13:51:34.400 に答える
0

RVO が可能かどうかは、ABI によって異なります。C++ 標準で許可されていても、実装の ABI では許可されない場合があります。Itanium C++ ABIの引用:

一般に、C++ の戻り値は C の戻り値と同じように処理されます。これには、レジスタに返されるクラス タイプの結果が含まれます。ただし、戻り値の型に自明でないコピー コンストラクターまたはデストラクターがある場合、呼び出し元は一時用に領域を割り当て、このパラメーターとユーザー パラメーターの両方に先行する暗黙的な最初のパラメーターとして一時的へのポインターを渡します。呼び出し先は、戻り値をこのテンポラリに構築します。

注: 名前にかかわらず、Itanium C++ ABI は Itanium だけで使用されるわけではありません。

また、参照されるベース C ABI は、構造体が特定のサイズである場合にレジスターに返されることを指定します。これは、質問の構造体(クラス)にも当てはまります。重要なコピー コンストラクターまたはデストラクターがあると、クラスをレジスターに返すことができなくなるため、ABI はさまざまな規則を指定する必要があり、それらのさまざまな規則の下で RVO が可能になります。

于 2014-12-21T20:16:46.487 に答える