Подтвердить что ты не робот

С++ 11 способ инициализации элементов данных из аргументов

Увидев, что С++ 11 поддерживает семантику переноса, при инициализации членов данных из аргументов мы должны попытаться перенести значение вместо его копирования?

Вот пример, показывающий, как я буду обращаться к этому в pre-С++ 11:

struct foo {
    std::vector<int> data;

    explicit foo(const std::vector<int>& data)
        : data(data)
    {
    }
};

Здесь будет вызываться конструктор копирования.

В С++ 11 мы должны привыкнуть писать так:

struct foo {
    std::vector<int> data;

    explicit foo(std::vector<int> data)
        : data(std::move(data))
    {
    }
};

Здесь конструктор перемещения будет вызываться... а также конструктор копирования, если переданный аргумент является значением lvalue, но преимущество в том, что если rvalue было передано, вместо этого будет вызываться конструктор перемещения.

Мне интересно, если что-то мне не хватает.

4b9b3361

Ответ 1

Мой первоначальный ответ на ваш вопрос:

Не копируйте данные, которые хотите переместить. Вы можете добавить конструктор, используя ссылку rvalue, если производительность является проблемой:

explicit foo(std::vector<int>&& data)
    : data(std::move(data))            // thanks to Kerrek SB
{
}

Не точно соответствует вашему вопросу, но читайте Правило-тройка становится Правилом пяти с С++ 11? представляется полезным.

Edit:

Однако принятый ответ на Передача/перемещение параметров конструктора в С++ 0x похоже, защищает ваш подход, особенно с несколькими параметрами. В противном случае был бы комбинаторный взрыв вариантов.

Ответ 2

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

Передача по значению имеет смысл для оператора присваивания, если у вас есть правильно реализованная функция swap, хотя:

Foo & operator=(Foo other) { this->swap(std::move(other)); }

Теперь, если other является подвижным, в конструкцию аргументов входит конструктор move Foo, и если other просто копируется, то необходимая копия создается во время построения аргумента, но в обоих случаях вы можете использовать движущаяся версия swap, которая должна быть дешевой. Но это зависит от существования конструктора перемещения!

Итак, обратите внимание, что из "построения", "свопа" и "ассимиляции" вам придется реализовать два правильно, и только третье может воспользоваться двумя другими. Поскольку swap не должен быть броском, использование трюка подкачки в операторе привязки в основном является единственным вариантом.

Ответ 3

Да, вы делаете это правильно. Каждый раз, когда вам нужна копия значения, сделайте это в параметрах, пройдя по значению.

Правильно:

struct foo {
    std::vector<int> data;

    explicit foo(std::vector<int> data)
        : data(std::move(data))
    {
    }
};

Ответ 4

Вам следует придерживаться:

struct foo {
    std::vector<int> data;

    explicit foo(const std::vector<int>& data)
        : data(data)
    {
    }
};

В этом случае "данные" копируются только.

Во втором случае:

struct foo {
    std::vector<int> data;

    explicit foo(std::vector<int> data)
        : data(std::move(data))
    {
    }
};

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

С другой стороны, вы можете рассмотреть возможность добавления следующего (помимо или вместо первого).

struct foo {
    std::vector<int> data;

    explicit foo(std::vector<int>&& data)
        : data(std::move(data))
    {
    }
};

Если вы знаете, что "данные" не будут использоваться после вызова конструктора, и в этом случае вы можете просто переместить его.