2

Microsoft Visual C ++(Microsoft Visual Studio 2012 RC、バージョン11.0.50522.1 RCREL)のカスタム配列の次のクラステンプレートについて考えてみましょう。

/*C++11 switch-on*/

#include <iostream>

template <typename element, unsigned int size>
class array
{
    private:
        element data[size];
    public:
        array(){}
        ~array(){}
        array(const array & other)(){}
        element & operator [](unsigned int i)
        {
            if(i<size)
                return data[i];
            else
                throw std::runtime_error("Out of boundary");
        }
}

コンストラクタ、デストラクタ、およびコピーコンストラクタは何もしないように定義されていることに注意してください。簡単な印刷機能は次のように定義されます

/*printing*/

template <typename element, unsigned int size>
void print(test::array<element, size> & content)
{
    unsigned int i=0;
    for(std::cout<<"["<<content[i++];i<size;std::cout<<content[i++])
        std::cout<<",";
    std::cout<<"]"<<std::endl;
}

プログラムが実行されるとき次のメイン

int main(int argc, char * argv[])
{
    array<int, 3> a;

    /* uniform initialization is not supported yet
     * so we bother iterating to assign to initialize
     * a to [1,2,3]
    */

    for(int i=0;i<3;i++)
        a[i]=i+1;

    /*copy*/
    auto b=a;

    /*move*/
    auto c=std::move(a);

    /*change in a*/
    a[0]=0;

    print<int, 3>(a);
    print<int, 3>(b);
    print<int, 3>(c);

    return 0;
}

コンパイルの最適化によって、出力が異なることがわかります。特に、コンパイルして実行すると

  • /Odスイッチをオンにした状態

    a = [0,2,3]

    b = [1470797225、-2,9185596]

    c = [0,2620008,9186761]

  • / O1、/ O2、または/Oxスイッチがオンの場合

    a = [0,2,3]

    b = [0,2,3]

    c = [0,2,3]

今私はそれを理解しています

  • /Odスイッチをオンにした状態
    • bはaとは異なります。これは、コピーコンストラクターが呼び出されても何も行わないためです。
    • cは、コピーコンストラクターが呼び出されても何もしないため、aとは異なります。しかし、移動セマンティクスによれば、aの配列データの要素の変更はcにも反映されます。したがって、a [0] == c [0]==0です。

しかし、最適化スイッチをオンにすると、 abcがすべて等しい理由がわかりません。Microsoft C ++コンパイラは、何もしないコピーコンストラクタを移動するコピーコンストラクタに置き換えると思うかもしれませんが、それについてはよくわかりません。

4

1 に答える 1

6

[conv.lval]規格のセクションでは、次のように定めています。

非関数、非配列型のglvalueはT、prvalueに変換できます。Tが不完全なタイプの場合、この変換を必要とするプログラムの形式が正しくありません。glvalueが参照するオブジェクトが型のオブジェクトでTはなく、から派生した型のオブジェクトでもない場合Tまたはオブジェクトが初期化されていない場合、この変換を必要とするプログラムの動作は未定義です。Tが非クラスタイプの場合、prvalueのタイプはcv-unqualifiedバージョンのですT。それ以外の場合、prvalueのタイプはですT

内部printでは、式cout << content[i++]は左辺値から右辺値への変換を使用します。print(b)またはを呼び出すprint(c)と、変換は初期化されていないオブジェクトで行われるため、未定義の動作が発生します。

未定義の振る舞いを特徴付けようとすることは、無益な運動です。


注:オブジェクトbcは、コピーコンストラクターによって初期化されます。b.contentコピーコンストラクターはサブオブジェクトまたはを初期化しませんc.content。つまり、これらの配列とそのすべてのメンバー要素は正式には初期化されていませ

aを初期化するとき、コードは実際には移動しませんcstd::move移動を可能にする右辺値参照を作成しますが、右辺値参照を受け入れる一致するコンストラクターがないため、コピーコンストラクターが使用され、aコピーされ、移動されません。

于 2012-07-04T15:08:48.293 に答える