Разница в эффективности между конструктором копирования и перемещения
C++11 представил новую концепцию ссылки на rvalue. Я где-то читал и нашел следующее:
class Base
{
public:
Base() //Default Ctor
Base(int t) //Parameterized Ctor
Base(const Base& b) //Copy Ctor
Base(Base&& b) //Move Ctor
};
void foo(Base b) //Function 1
{}
void foo(Base& b) //Function 2
{}
int main()
{
Base b(10);
foo(b); -- Line 1 (i know of ambiquity but lets ignore for understanding purpose)
foo(Base()); -- Line 2
foo(2) ; -- Line 3
}
Теперь, с моим ограниченным пониманием, мои наблюдения таковы:
Строка 1 просто вызывает конструктор копирования, поскольку аргумент является lvalue.
Строка 2 перед C++11 вызывала бы конструктор копирования и все эти временные вещи копирования, но с определенным конструктором перемещения, который будет вызван здесь.
Строка 3 снова вызовет конструктор перемещения, так как 2 будет неявно преобразована в базовый тип (rvalue).
Пожалуйста, исправьте и объясните, если какое-либо из вышеприведенных замечаний неверно.
Теперь вот мои вопросы:
Я знаю, что когда мы переместим объект, его данные будут потеряны в месте вызова. Итак, я выше пример, как я могу изменить строку 2, чтобы переместить объект "b" в foo (это с помощью std::move(b)?).
Я читал, что конструктор перемещения более эффективен, чем конструктор копирования. Как? Я могу думать только о ситуации, когда у нас есть память в куче, которую не нужно распределять снова в случае конструктора перемещения. Верно ли это утверждение, когда у нас нет памяти в куче?
Это даже более эффективно, чем передача по ссылке (нет, верно?)?
2 ответа
Сначала о ваших "пониманиях":
Насколько я понимаю, они в принципе правы, но вам следует помнить об исключении копирования, которое может помешать программе вызывать любой конструктор копирования / перемещения. Зависит от вашего компилятора (-settings).
На ваши вопросы:
Да, вы должны вызвать foo(std::move(b)), чтобы вызвать функцию, которая принимает значение со значением lvalue. std:: move выполнит приведение. Примечание: сам std:: move ничего не перемещает.
Использование Move-Constructor "может" быть более эффективным. По правде говоря, это только позволяет программистам реализовывать более эффективные конструкторы. В качестве примера рассмотрим вектор, который является классом вокруг указателя на массив, который содержит данные (аналогично std::vector), если вы копируете его, вы должны скопировать данные, если вы переместите его, вы можете просто передать указатель и установить старый в nullptr. Но, как я читал в " Эффективном современном C++ " Скотта Мейерса: не думайте, что ваша программа будет работать быстрее только потому, что вы всегда используете std:: move.
Это зависит от использования ввода. Если вам не нужна копия в функции, в большинстве случаев будет более эффективно просто передать объект по (const) ссылке. Если вам нужна копия, есть несколько способов сделать это, например, идиома копирования и обмена.
Но как
Строка 2 перед C++11 вызывала бы конструктор копирования и все эти временные вещи копирования, но с определенным конструктором перемещения, который будет вызван здесь.
Правильно, за исключением того, что любой приличный оптимизатор "удалит" копию, так что до C++11 копии можно было бы избежать, а после C++11 такого перемещения было бы избежать. То же самое для линии 3.
- Я знаю, что когда мы переместим объект, его данные будут потеряны в месте вызова.
Зависит от того, как реализован конструктор перемещения. Если вы не знаете, это то, что вы должны принять.
Итак, я выше пример, как я могу изменить строку 2, чтобы переместить объект "b" в foo (это с помощью std::move(b)?).
Именно так. std::move
изменяет тип выражения на r-значение и, следовательно, вызывается конструктор перемещения.
Я читал, что конструктор перемещения более эффективен, чем конструктор копирования.
Это может быть, в некоторых случаях. Например, конструктор перемещения std::vector
гораздо быстрее, чем копировать.
Я могу думать только о ситуации, когда у нас есть память в куче, которую не нужно распределять снова в случае конструктора перемещения. Верно ли это утверждение, когда у нас нет памяти в куче?
Это утверждение не всегда верно, так как для объектов с тривиальным конструктором копирования конструктор перемещения уже не эффективен. Но владение динамической памятью не является обязательным требованием для более эффективного движения. В более общем смысле, перемещение может быть эффективным, если объект владеет каким-либо внешним ресурсом, который может быть динамической памятью, или это может быть, например, счетчик ссылок или дескриптор файла, который должен быть освобожден в деструкторе и, следовательно, повторно найден или повторно рассчитано на копию - чего можно избежать на ходу.
Это даже более эффективно, чем передача по ссылке (нет, верно?)?
На самом деле нет. Однако, если вы намереваетесь переместить объект в функцию, куда вы передаете его по ссылке, вам придется передать неконстантную ссылку и, следовательно, не сможет передать временные значения.
Вкратце: Ссылка отлично подходит для предоставления временного доступа к объекту, который вы храните, перемещение отлично подходит для передачи права собственности.