6

私はWant Speedを読んでいましたか?C++ Next ブログのPass by Valueでは、C++0x でのコピー省略と移動セマンティクスの感触をつかむために次のプログラムを作成しました。

#include <vector>
#include <iostream>

class MoveableClass {
public:
    MoveableClass() : m_simpleData(0), instance(++Instances) {
        std::cout << "Construct instance " << instance << " (no data)" << std::endl;
    }

    MoveableClass(std::vector<double> data) : m_data(std::move(data)), m_simpleData(0), instance(++Instances) {
        std::cout << "Construct instance " << instance << " (with data)" << std::endl;
    }

    MoveableClass(int simpleData) : m_simpleData(simpleData), instance(++Instances) {
        std::cout << "Construct instance " << instance << " (with simple data)" << std::endl;
    }

    MoveableClass(const MoveableClass& other) 
        : m_data(other.m_data), m_simpleData(other.m_simpleData), instance(++Instances)
    {
        std::cout << "Construct instance " << instance << " from a copy of " << other.instance << std::endl;
        Elided = false;
    }

    MoveableClass(MoveableClass&& other) 
        : m_data(std::move(other.m_data)), m_simpleData(other.m_simpleData), instance(++Instances)
    {
        std::cout << "Construct instance " << instance << " from a move of " << other.instance << std::endl;
        Elided = false;
    }

    MoveableClass& operator=(MoveableClass other) {
        std::cout << "Assign to instance " << instance << " from " << other.instance << std::endl;
        other.Swap(*this);
        return *this;
    }

    ~MoveableClass() {
        std::cout << "Destroy instance " << instance << std::endl;
        --Instances;
    }

    void Swap(MoveableClass& other) {
        std::swap(m_data, other.m_data);
        std::swap(m_simpleData, other.m_simpleData);
    }

    static int Instances;
    static bool Elided;

private:
    int instance;
    int m_simpleData;
    std::vector<double> m_data;
};

int MoveableClass::Instances = 0;
bool MoveableClass::Elided = true;

std::vector<double> BunchOfData() {
    return std::vector<double>(9999999);
}

int SimpleData() {
    return 9999999;
}

MoveableClass CreateRVO() {
    return MoveableClass(BunchOfData());
}

MoveableClass CreateNRVO() {
    MoveableClass named(BunchOfData());
    return named;
}

MoveableClass CreateRVO_Simple() {
    return MoveableClass(SimpleData());
}

MoveableClass CreateNRVO_Simple() {
    MoveableClass named(SimpleData());
    return named;
}

int main(int argc, char* argv[]) {
    std::cout << "\nMove assign from RVO: " << '\n';
    {
        MoveableClass a;
        a = CreateRVO();
    }
    std::cout << "Move elided: " << (MoveableClass::Elided ? "Yes" : "No") << '\n';
    MoveableClass::Elided = true;   // reset for next test

    std::cout << "\nMove assign from RVO simple: " << '\n';
    {
        MoveableClass a;
        a = CreateRVO_Simple();
    }
    std::cout << "Move elided: " <<  (MoveableClass::Elided ? "Yes" : "No") << '\n';
    MoveableClass::Elided = true;   // reset for next test

    std::cout << "\nMove assign from NRVO: " << '\n';
    {
        MoveableClass a;
        a = CreateNRVO();
    }
    std::cout << "Move elided: " <<  (MoveableClass::Elided ? "Yes" : "No") << '\n';
    MoveableClass::Elided = true;   // reset for next test

    std::cout << "\nMove assign from NRVO simple: " << std::endl;
    {
        MoveableClass a;
        a = CreateNRVO_Simple();
    }
    std::cout << "Move elided: " << (MoveableClass::Elided ? "Yes" : "No") << '\n';
    MoveableClass::Elided = true;   // reset for next test
}

Visual C++ 10.0 (ベータ 2) でリリース モードでコンパイルしたときに得られる出力は次のとおりです。


インスタンス 1 を構築する (データなし)
インスタンス 2 を構築する (データあり)
2 の移動からインスタンス 3 を構築する インスタンス
2を破棄する3 から
インスタンス 1 に割り当てるインスタンス
3を破棄する インスタンス
1を破棄する
省略された移動: いいえ

Move assign from RVO simple:
インスタンス 1 の構築 (データなし)
インスタンス 2 の構築 (単純なデータあり)
インスタンス 1 から 2 への割り当て
インスタンス 2の
破棄 インスタンス 1の破棄
省略された移動: はい

NRVO からの割り当ての移動:
インスタンス 1 の構築 (データなし)
インスタンス 2 の構築 (データあり) 2
からインスタンス 1 への割り当て
インスタンス 2の
破棄 インスタンス 1の破棄
省略された移動: はい

NRVO シンプルからの割り当ての移動:
インスタンス 1 の構築 (データなし)
インスタンス 2 の構築 (単純なデータあり)
インスタンス 1 から 2 への割り当て
インスタンス 2の
破壊 インスタンス 1の破壊
省略された移動: はい

しかし、私は一つのことに困惑しています。ご覧のとおり、最初の動きを除いてすべての動きが省略されています。コンパイラが 86 行目の MoveableClass(std::vector) では RVO を実行できないのに、97 行目の MoveableClass(int) では実行できるのはなぜですか? これは単なる MSVC のバグですか、それとも正当な理由がありますか? 正当な理由がある場合、91 行目で MoveableClass(std::vector) に対して NRVO を実行できるのはなぜですか?

幸せな気持ちで眠れるように理解したいです。:)

4

3 に答える 3

2

デイブに返信していただきありがとうございます。

その例にテストを追加しました:
pastebin.com/f7c8ca0d6

不思議なことに、NRVOを除いてすべてのタイプのエリジオンが実行されていないことを示しています!
編集:実際には、これはオブジェクトに名前が付けられている唯一のテストであるためだと思います。

他のSTLタイプも試しましたが、同じ結果が得られました。ただし、ポッド以外のタイプを試してみると、期待どおりに機能します。これを引き起こしている可能性のあるSTLタイプの何が特別なのかわからないので、他に何を試すべきかわかりません。

バグレポートを提出します。
編集:ここに提出

ありがとう

于 2009-11-08T11:42:25.067 に答える
1

うーん。

データコンストラクタを変更すると

MoveableClass::MoveableClass(std::vector<double> data)

そのように、参照によってベクトルを受け入れるには、

MoveableClass::MoveableClass(const std::vector<double>& data) 

それはうまくいきます!ベクトルを値で渡すと機能しないのはなぜですか?

また、以前のバージョンの MSVC でコンパイルする必要があるバージョンもあります。そこでテストを実行したい場合は、. C++0x 機能は含まれていません: http://pastebin.com/f3bcb6ed1

于 2009-11-07T14:16:05.970 に答える
0

おそらく、 cpp-nextからのこの例を、失敗したバージョンのテストで更新して維持することをお勧めします。これにより、1 つの包括的で標準的なテストが可能になります。

于 2009-11-08T09:15:24.910 に答える