«Вау, C++ 11 просто великолепен! Они представили ссылки rvalue и семантику перемещения. Пока я определяю конструктор перемещения и вызываю std::move, все будет идеально! Я думаю, что ссылки std::move и rvalue могут даже вылечить рак. Да здравствует C++ и std::move !!

Звучит знакомо? После прочтения глав о rvalues, lvalues, справочнике по rvalue и мощном волшебном вызове std::move, Разве это не то, что вы думали о семантике движений? Простите, что разочаровал вас, ребята/девушки, вы можете ошибаться в своем понимании. Тссс, слушай… позвольте мне раскрыть этот секрет:

Могучий волшебный std::move ничего НЕ перемещает!!!.

"О чем ты говоришь? Чувак, ты должен снова выучить C++, прежде чем писать здесь ахинею!»

Это то, о чем ты думаешь? Хорошо, позвольте мне доказать, что я прав, и вы можете ошибаться. Давайте напишем идеальныйкод C++, следуя всем этим рекомендациям.

Предположим, у меня есть класс Widget, который принимает метку в своем конструкторе как ссылку на rvalue. Label – это еще один определяемый пользователем класс с конструктором копирования и конструктором перемещения. Вот фрагмент кода:

  • Как видно из этого фрагмента кода, экземпляр rvalueLabel передается конструктору виджета.
  • Конструктор виджета принимает «ссылку на rvalue» в качестве параметра. Как идеальная практика в C++, чтобы избежать создания копий, он вызывает конструктор перемещения Label, вызывая std::move. Как мы все знаем, функция std::move никогда не дает сбоев!
  • В Label есть конструктор копирования ("этот чувак никогда не будет выполняться в этом контексте. Бедный конструктор копирования, иди и найди лучшую жизнь!") и конструктор перемещения("О мой любимый! Люблю тебя!! Мой идеальный код обязательно вызовет тебя!")

Хорошо, 1..2..3.. Давайте выполним этот фрагмент кода:

"Что за **** !!!! Почему std::move вызывает конструктор копирования, а не конструктор перемещения? Боже мой код компилятора поврежден!!! “

Расслабься, чувак, в компиляторе C++ нет ничего плохого. Давайте отладим и выясним, что пошло не так.

Позвольте мне повторить. Могучий волшебный std::move ничего не перемещает.

«Хорошо, приятель, я думаю, ты прав. Объясни мне это, просвети меня… что здесь пошло не так?»

Вот ответ. Мощный вызов std::move ничего не перемещает. Это просто просьба. Точнее, это запрос приведения для приведения аргумента к rvalue. Ни больше ни меньше. Если бы я был программистом компилятора C++ 11, я бы даже переименовал эту функцию в rvalue_castвместо move.

«Хорошо, достаточно честно. Но, пожалуйста… пожалуйста… пожалуйста… скажите мне, что пошло не так с нашим идеальным кодом?»

Давайте еще раз вернемся к этому коду.

Widget widget(Label("Rectangle"));// Здесь все в порядке

Следующий шаг: конструктор виджета

Widget(constLabel&& label) :m_label(std::move(label)) { std::cout ‹‹ «Конструктор виджета» ‹‹ std::endl;

Он принимает ссылку constrvalue на метку и вызывает std::move для этого экземпляра метки. А вот и конструктор перемещения Label:

Метка(Ярлык&&правая) {… }

Что именно ты здесь делаешь? Вы выполняете std::move с константным ссылочным параметром, где его подходящий конструктор перемещения ожидает неконстантный ссылочный параметр ! !! Если я упрощу это утверждение, вы просите std::move нарушить константную корректность. Конечно, std::move этого не сделает! Какого черта он должен подчиняться твоей глупой просьбе?

Решение здесь действительно простое. Просто измените аргумент конструктора перемещения метки на ссылку constrvalue.

Label(constLabel&& rhs) {…}

Вот обновленный код и давайте запустим эту версию

Теперь все выглядит хорошо. Поздравляем! Вы узнали что-то действительно полезное, поверьте мне!

Заключение. Как я уже говорил, std::move — это просто операция приведения. Он отлично работает, как мы и ожидали, если его аргумент является ссылкой на rvalue и соответствующий конструктор перемещения ожидает точно такой же тип аргумента (включая константность).

Независимо от того, является ли его аргумент ссылкой на lvalue или ссылкой на rvalue, std::move просто приводит к ссылке на rvalue. Вот и все.

Он не способен выполнять какую-либо константную корректность. Банальная ошибка в вашем фрагменте кода, как в этом случае, может привести к катастрофическим последствиям.

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

На сегодня все, ребята. Я вернусь позже с другой ловушкой. Быть в курсе. Спасибо за чтение! Ваше здоровье