В чем преимущество использования конструктора перемещения по сравнению с передачей указателя?

Foo(Foo&& other) {
  this->bar = other.bar;
  other.bar = nullptr;
}

Foo(Foo* other) {
  this->bar = other->bar;
  other->bar = nullptr;
}

Вышеупомянутые два, кажется, делают то же самое. Так почему рекомендуется использовать конструктор перемещения? Какие преимущества это дает?

1 ответ

Причин для использования правильного конструктора перемещения много:

  1. Конвенция. Рассмотрим следующий код:

    Foo foo;
    Foo bar(std::move(foo));
    

    Если вы видите этот код, совершенно ясно, что вы пытаетесь сделать: вы двигаетесь foo в bar, В отличие от:

    Foo foo;
    Foo bar(&foo);
    

    Что это значит? Хранит ли указатель на другой Foo? Является Foo какой-то тип узла связанного списка? Вы должны найти это в документации для Fooоснованный на указателе конструктор.

  2. Вы не можете получить указатель на значения. Foo bar(Foo{}) будет (до C++17) создать временное значение prvalue, а затем перейти от него в bar (ход может быть исключен до C++17). Но вы не можете сделать это: Foo bar(&Foo{}),

  3. Вы получаете автоматическое движение от вещей, где это имеет смысл. Обобщенное исключение в C++ 17 устраняет многие из этих автоматических движений, но есть некоторые, которые остаются:

    Foo function()
    {
      Foo foo;
      //Do stuff
      return foo;
    }
    

    Это будет двигаться от foo без необходимости звонить std::move, Причина в том, что foo является локальной переменной, которая собирается быть уничтоженной. Возвращать его таким образом означает, что вполне разумно перейти от него к возвращаемому значению.

    Ваш путь потребует явного написания return &foo, И это делает вещи довольно сложными с auto возврат типа удержания. Такая функция выведет Foo*, что означает, что вы только что вернули висячий указатель. Принимая во внимание, что если вы делаете return foo;, это выводит Foo prvalue, с автоматическим движением (которое может быть исключено).

  4. Нет необходимости nullptr чеки. Если вы берете Foo*, возможно, что кто-то передаст нулевой указатель. В то время как со ссылкой, это гораздо менее вероятно (и они уже вызвали UB).

Если бы мы хотели переместить поддержку существующих языковых конструкций, мы бы просто использовали неconst Ссылка означает "двигаться", а не указатель. Придав ему собственный синтаксис, проясните, когда мы что-то перемещаем, а когда нет.

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