Скопируйте исключение в Visual C++ 2010 Beta 2

Я читал Хотите скорость? Передайте по значению в блоге C++ Next и создайте эту программу, чтобы понять семантику исключения копирования и перемещения в 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):

Перемещение назначается из RVO:
Создание экземпляра 1 (без данных)
Создание экземпляра 2 (с данными)
Создание экземпляра 3 из перемещения 2
Уничтожение экземпляра 2
Назначение экземпляра 1 из 3
Уничтожить экземпляр 3
Уничтожить экземпляр 1
Переместить опущено: Нет

Переместить назначение из простого RVO:
Создать экземпляр 1 (без данных)
Создать экземпляр 2 (с простыми данными)
Назначить экземпляру 1 из 2
Уничтожить экземпляр 2
Уничтожить экземпляр 1
Перемещение опущено: Да

Переместить назначение из NRVO:
Создать экземпляр 1 (без данных)
Создать экземпляр 2 (с данными)
Назначить экземпляру 1 из 2
Уничтожить экземпляр 2
Уничтожить экземпляр 1
Переместить опущено: да

Переместить назначение из простого NRVO:
Создать экземпляр 1 (без данных)
Создать экземпляр 2 (с простыми данными)
Назначить экземпляру 1 из 2
Уничтожить экземпляр 2
Уничтожить экземпляр 1
Перемещение опущено: Да

Однако меня смущает одно. Как видите, все ходы пропущены, кроме первого. Почему компилятор не может выполнить RVO с MoveableClass(std::vector) в строке 86, но может с MoveableClass(int) в строке 97? Это просто ошибка с MSVC или для этого есть веская причина? И если есть веская причина, почему он все еще может выполнять NRVO для MoveableClass(std::vector) в строке 91?

Я хотел бы понять это, чтобы я мог заснуть счастливым. :)


person dvide    schedule 07.11.2009    source источник
comment
Очень хороший вопрос. Что бы это ни стоило, g++ 4.3.3 игнорирует все эти ходы, даже с флагом -O0.   -  person Thomas    schedule 07.11.2009
comment
Спасибо, Томас. Интересно, что это работает на GCC. Возможно, это говорит о том, что что-то не так с реализацией MSVC.   -  person dvide    schedule 07.11.2009
comment
Я думаю, это только подчеркивает, насколько велика пропасть между компилятором и компилятором.   -  person Crashworks    schedule 08.11.2009


Ответы (3)


Спасибо за ответ Дэйв.

Я добавил к этому примеру свои тесты:
pastebin.com/f7c8ca0d6

Любопытно, что он показывает, что не выполняются все типы исключений, кроме NRVO!
Редактировать: На самом деле, я полагаю, это потому, что это единственный тест, в котором у объекта когда-либо есть имя.

Я также пробовал другие типы STL и получил тот же результат. Однако при попытке использовать мои собственные типы, отличные от pod, он работает, как и ожидалось. Я не могу понять, что особенного в типах STL, которые могут быть причиной этого, поэтому я не знаю, что еще попробовать.

Я отправлю отчет об ошибке.
Изменить: Отправлено здесь< /а>

Спасибо

person dvide    schedule 08.11.2009

Хм.

Кажется, что если вы измените конструктор данных

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

чтобы принять вектор по ссылке, вот так,

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

это работает нормально! Почему не работает, если передать вектор по значению?

Также вот версия, которая должна компилироваться в более ранних версиях MSVC, если кто-то хочет запустить там тест. Он не содержит функций C++0x: http://pastebin.com/f3bcb6ed1

person dvide    schedule 07.11.2009

Возможно, было бы неплохо обновить и поддерживать этот пример. из cpp-next с версией вашего теста, которая не удалась, поэтому может быть один комплексный, Канонический тест.

person Dave Abrahams    schedule 08.11.2009