Разница в эффективности между конструктором копирования и перемещения

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. Строка 1 просто вызывает конструктор копирования, поскольку аргумент является lvalue.

  2. Строка 2 перед C++11 вызывала бы конструктор копирования и все эти временные вещи копирования, но с определенным конструктором перемещения, который будет вызван здесь.

  3. Строка 3 снова вызовет конструктор перемещения, так как 2 будет неявно преобразована в базовый тип (rvalue).

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

Теперь вот мои вопросы:

  1. Я знаю, что когда мы переместим объект, его данные будут потеряны в месте вызова. Итак, я выше пример, как я могу изменить строку 2, чтобы переместить объект "b" в foo (это с помощью std::move(b)?).

  2. Я читал, что конструктор перемещения более эффективен, чем конструктор копирования. Как? Я могу думать только о ситуации, когда у нас есть память в куче, которую не нужно распределять снова в случае конструктора перемещения. Верно ли это утверждение, когда у нас нет памяти в куче?

  3. Это даже более эффективно, чем передача по ссылке (нет, верно?)?

2 ответа

Сначала о ваших "пониманиях":

Насколько я понимаю, они в принципе правы, но вам следует помнить об исключении копирования, которое может помешать программе вызывать любой конструктор копирования / перемещения. Зависит от вашего компилятора (-settings).

На ваши вопросы:

  1. Да, вы должны вызвать foo(std::move(b)), чтобы вызвать функцию, которая принимает значение со значением lvalue. std:: move выполнит приведение. Примечание: сам std:: move ничего не перемещает.

  2. Использование Move-Constructor "может" быть более эффективным. По правде говоря, это только позволяет программистам реализовывать более эффективные конструкторы. В качестве примера рассмотрим вектор, который является классом вокруг указателя на массив, который содержит данные (аналогично std::vector), если вы копируете его, вы должны скопировать данные, если вы переместите его, вы можете просто передать указатель и установить старый в nullptr. Но, как я читал в " Эффективном современном C++ " Скотта Мейерса: не думайте, что ваша программа будет работать быстрее только потому, что вы всегда используете std:: move.

  3. Это зависит от использования ввода. Если вам не нужна копия в функции, в большинстве случаев будет более эффективно просто передать объект по (const) ссылке. Если вам нужна копия, есть несколько способов сделать это, например, идиома копирования и обмена. Но как

Строка 2 перед C++11 вызывала бы конструктор копирования и все эти временные вещи копирования, но с определенным конструктором перемещения, который будет вызван здесь.

Правильно, за исключением того, что любой приличный оптимизатор "удалит" копию, так что до C++11 копии можно было бы избежать, а после C++11 такого перемещения было бы избежать. То же самое для линии 3.


  1. Я знаю, что когда мы переместим объект, его данные будут потеряны в месте вызова.

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

Итак, я выше пример, как я могу изменить строку 2, чтобы переместить объект "b" в foo (это с помощью std::move(b)?).

Именно так. std::move изменяет тип выражения на r-значение и, следовательно, вызывается конструктор перемещения.

Я читал, что конструктор перемещения более эффективен, чем конструктор копирования.

Это может быть, в некоторых случаях. Например, конструктор перемещения std::vector гораздо быстрее, чем копировать.

Я могу думать только о ситуации, когда у нас есть память в куче, которую не нужно распределять снова в случае конструктора перемещения. Верно ли это утверждение, когда у нас нет памяти в куче?

Это утверждение не всегда верно, так как для объектов с тривиальным конструктором копирования конструктор перемещения уже не эффективен. Но владение динамической памятью не является обязательным требованием для более эффективного движения. В более общем смысле, перемещение может быть эффективным, если объект владеет каким-либо внешним ресурсом, который может быть динамической памятью, или это может быть, например, счетчик ссылок или дескриптор файла, который должен быть освобожден в деструкторе и, следовательно, повторно найден или повторно рассчитано на копию - чего можно избежать на ходу.

Это даже более эффективно, чем передача по ссылке (нет, верно?)?

На самом деле нет. Однако, если вы намереваетесь переместить объект в функцию, куда вы передаете его по ссылке, вам придется передать неконстантную ссылку и, следовательно, не сможет передать временные значения.

Вкратце: Ссылка отлично подходит для предоставления временного доступа к объекту, который вы храните, перемещение отлично подходит для передачи права собственности.

Другие вопросы по тегам