Как правильно написать Copy / Move / operator = trio в C ++ 11? - PullRequest
9 голосов
/ 23 февраля 2012

На этом этапе запись пары конструктор копирования и оператор присваивания четко определена; быстрый поиск приведет к большому количеству обращений о том, как правильно их кодировать.

Теперь, когда конструктор движения вошел в микс, есть ли новый "лучший" способ?

Ответы [ 4 ]

12 голосов
/ 23 февраля 2012

Предпочтительно, они будут просто = default;, так как типы элементов должны относиться к типам управления ресурсами, которые скрывают от вас детали перемещения, например std::unique_ptr. Только разработчики этих типов «низкого уровня» должны иметь дело с этим.

Помните, что вам нужно беспокоиться только о семантике перемещения, если вы держите внешний (для вашего объекта) ресурс. Это совершенно бесполезно для "плоских" типов.

5 голосов
/ 23 февраля 2012

Лучший способ - позволить компилятору сгенерировать их все.Это был также лучший подход в C ++ 03, и если вам удалось это сделать, ваши классы C ++ 03 автоматически становятся «включенными» при переходе на C ++ 11.

Большинство проблем управления ресурсамиможно решить, написав только не копирующие конструкторы и деструкторы классов управления одним ресурсом, а затем создавая только составные классы, используя их, плюс умные указатели (например, std::unique_ptr) и контейнерные классы для создания более богатых объектов.

4 голосов
/ 23 февраля 2012

Использование clang / libc ++:

#include <chrono>
#include <iostream>
#include <vector>

#if SLOW_DOWN

class MyClass
{
    void Swap(MyClass &other)
    {
        std::swap(other.member, member);
    }

public:
    MyClass()
        : member()
    {
    }

    MyClass(const MyClass &other)
        : member(other.member)
    {
    }

    MyClass(MyClass &&other)
        : member(std::move(other.member))
    {
    }

    MyClass &operator=(MyClass other)
    {
        other.Swap(*this);
        return *this;
    }

private:
    int member;
};

#else

class MyClass
{
public:
    MyClass()
        : member()
    {
    }

private:
    int member;
};

#endif

int main()
{
    typedef std::chrono::high_resolution_clock Clock;
    typedef std::chrono::duration<float, std::milli> ms;
    auto t0 = Clock::now();
    for (int k = 0; k < 100; ++k)
    {
        std::vector<MyClass> v;
        for (int i = 0; i < 1000000; ++i)
            v.push_back(MyClass());
    }
    auto t1 = Clock::now();
    std::cout << ms(t1-t0).count() << " ms\n";
}

$ clang++ -stdlib=libc++ -std=c++11 -O3 -DSLOW_DOWN test.cpp 
$ a.out
519.736 ms
$ a.out
517.036 ms
$ a.out
524.443 ms

$ clang++ -stdlib=libc++ -std=c++11 -O3  test.cpp 
$ a.out
463.968 ms
$ a.out
458.702 ms
$ a.out
464.441 ms

Это похоже на разницу скорости примерно в 12% в этом тесте.

Объяснение: В одном из этих определений есть тривиальный конструктор копирования и оператор назначения копирования,Другой нет.«Тривиальный» имеет реальное значение в C ++ 11.Это означает, что реализация может использовать memcpy для копирования вашего класса.Или даже скопировать большие массивы вашего класса.Поэтому лучше, если можете, сделать своих специальных членов тривиальными.Это значит позволить компилятору определить их.Хотя вы все равно можете объявить их с = default, если хотите.

2 голосов
/ 23 февраля 2012

Это то, что я придумал, но я не знаю, есть ли более оптимальное решение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...