Здравствуйте уважаемые читатели. Данная публикация адресована начинающим разработчикам С++ которые только становятся на путь высокой производительности и "отстрелянных конечностей". Опытные разработчики найдут здесь скорее повторение тех вещей, которые сами мучительно осваивали в те далекие времена, когда в языке С++ появилась возможность удобного перемещения объектов. Многие из вас уже слышали и надеюсь использовали функцию move() в своих проектах. Если нет, то пришло время с ней познакомиться. Вопрос: Опять этот move, сколько уже можно? Есть же множество опубликованного материала по этой теме? Ответ: Да, есть много статей. В свое время учился по ним, в том числе и тут, на Хабре [1, 2]. Но мне все равно было не понятно, значит, учитывая статистику, непонятно также и некоторому количеству читателей. Как обычно начинаются туториалы по move? Рассмотрим lvalue объект, ему соответствует rvalue объект, между ними есть оператор присваивания (=). Тут появляются ссылки, да не просто, а ссылки на lvalue, на rvalue и пошло-поехало. Мозг перегружается, статья пролистывается до конца. Поэтому попробую рассказать о move c другой стороны - в стиле "от практики к теории" - так, как хотел бы чтобы мне рассказали. Что обычно говорят о move? Это крутая штука, код с ней работает быстрее. А насколько  быстрее? Давайте проверим. Чтобы оценить быстродействие возьмем следующий класс: Не пугайтесь, он нам будет нужен только как условный секундомер для экспериментов. Чтобы с его помощью оценить время выполнения операции достаточно сделать так: где фигурные скобки задают область видимости. При выходе за ее пределы запускаются деструкторы классов для объектов (исключение - статичные переменные), которые были созданы внутри данной области, в том числе и ~LogDuration(), который покажет время выполнения операций внутри блока. Итак, начнем экспериментировать. Говорят, что для векторов и строк (std::string) нужно по возможности использовать move. Проверим. Напишем такой код: Здесь мы создаем вектор big_vector из нулей длиной 10^9, а затем создаем новый вектор как копию данного. Время на создание копии выводится в консоль: Программа valgrind показывает, что за время выполнения программы было использовано 2 ГБ оперативной памяти: Итак, у нас получилось два одинаковых вектора, затрачено полсекунды и 2 ГБ оперативной памяти. Дальше вопрос - а что если исходный вектор нам дальше в коде никогда не понадобится, мы бы сэкономили 1 ГБ. Давайте посмотрим, что будет если добавить move. Произведем замену: И о чудо! Время выполнения уменьшилось почти в 10 раз, а размер исходного вектора стал равен нулю: Valgrind уже более оптимистичен: Получается, что воспользовавшись move мы выиграли в скорости, но пожертвовали исходным вектором. Случай с длинной строкой вместо вектора предлагаю проверить самостоятельно. Теперь попробуем разобраться что тут вообще происходит. Давайте напишем свой вектор, точнее простую обертку над стандартным вектором  Также не пугайтесь, тут нужно смотреть на то, что внутри секции public. Добавьте этот код перед main() в вашей программе, а внутри main замените первую букву в слове vector на заглавную везде, где он упоминается. Для случая:  в консоли будет выведено: А для варианта с move: Здесь мы подходим к наблюдению, что функция move сама по себе не выполняет никаких перемещений, несмотря на название, а делает все возможное чтобы в данном конкретном примере вызвать конструктор перемещения - Vector(Vector&& rhs). Т.к. в приведенном классе-обертке в конструкторах выполяется только вывод текста, то понятно, что время операции столь мало, а исходный вектор никуда не исчезает. Использование move не ограничивается конструкторами классов. Например: Обратите внимание на строчку 12, где закомментирована операция. Сигнатура данной функции содержит "волшебные" символы && из-за которых не получается ей указать объект text. А какую-то бесхозную строку в кавычках можно. Теперь обратите внимание на строку 7, где объекту text присваивается "some text". Чем они различаются принципиально, кроме расположения лево-право от оператора присваивания?А тем, что text имеет адрес в памяти, а выражение "some text" его не имеет, точнее его адрес не так просто найти. Адрес постоянного объекта можно узнать так: Теперь смотрите, для того, чтобы функция MoveFoo приняла аргумент, он "не должен иметь адреса", как "another text" например. Такие объекты еще называют временными. Теперь мы можем подойти к тому моменту когда можно сказать, что делает функция move - она делает так, что ее аргумент притворяется "безадресным", т.е. временным, поэтому 14-я строка нормально компилируется. И если внутри функции MoveFoo ничего с text не делать, то он сам по себе никуда не пропадет, не перенесется, не исчезнет. Но зачем же тогда спрашивается все телодвижения? А вот если написать: то после выполнения данной функции переменная text во внешнем блоке окажется пустой (компилятор gcc 7.5 c++17), как в самом начале для случая с перемещением вектора. Теперь вернемся к вопросу почему исходный вектор "переместился" в новый вектор за такое короткое время? У нас есть некоторые наблюдения: при использовании move памяти затрачивалось практически вровень размеру исходного массива.Представим вектор как структуру данных, которая в самом упрощенном варианте хранит адрес (указатель) на место в памяти, где находятся все его элементы. Мы же помним, что в векторе все элементы расположены в памяти последовательно, без разрывов. А вторым полем будет переменная, хранящая текущий размер вектора. Также мы знаем, что после операции "перемещения" исходный вектор оказывается пустым. А теперь представьте, что встречаются два вектора - один с набором из 10^9 элементов, второй пустой. Самое простое решение им взять и "обменяться" своим содержимым. Новый просто изменит свой адрес, указывающий на начало блока данных на тот, что был у исходного. Также обновит свой размер. А исходный примет такие же поля от пустого вектора. Все просто. Если пройтись отладчиком по цепочке от конструктора перемещения, то можно обнаружить такой код в стандартной библиотеке в файле stl_vector.h: Там конечно, все намного сложнее, но общий принцип примерно таков. Очень надеюсь, что теперь основные моменты использования move для вас прояснились. Дальше рекомендую уже ознакомиться с более научными работами по использованию move семантики, где легко, надеюсь, уловите аналогии с lvalue, rvalue и т.п. А более опытным разработчикам - если дочитали до конца, буду рад услышать Ваши комментарии и замечания. Начинающий разработчик