С++ 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 было передано, вместо этого будет вызываться конструктор перемещения.
Мне интересно, если что-то мне не хватает.
Ответы
Ответ 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))
{
}
};
Если вы знаете, что "данные" не будут использоваться после вызова конструктора, и в этом случае вы можете просто переместить его.