В предыдущих видео мы познакомились с перемещением, и оно существенно помогало ускорить код экономив на копированиях. Но, важно понимать, что перемещение помогает не всегда, это вообще говоря не панацея, и давайте мы рассмотрим ситуации, в которых перемещения не помогает или помогает плохо. Вспомните, что когда мы рассказывали о том, как вообще работает перемещение и почему оно ускоряет код, мы рассказывали, что если у нас есть объект, у которого данные в куче, то эти данные можно просто в кучу оставить и перевести указатель тем самым по сути копирования этих данных не выполняет. Соответственно, нужно понимать, что если у объекта нет данных в куче и основные данные на стеке, то было бы перемещение или не было бы перемещения, все равно эти данные придется скопировать, потому что они лежат на стеке и их надо положить, например, в вектор. Какой вы знаете контейнер, который хранит свои данные на стеке? Конечно, это массив. Давайте на примере массива продемонстрируем, что перемещение не помогает ускорить работу с ним. Давайте подключим модуль Array, придумаем какой у нас должен быть размер этого массива, положим его в константу size, ну давайте это будет 10 тысяч, например, и напишем функцию, которая будет возвращать большой массив. Вот скажем массив, int off, размера size, функция называется MakeArray, и здесь создается этот самый массив, ну и скажем, мы его заполняем восьмерками, чтобы там были не нули. Возвращаем этот массив из функций и опять же попробуем создать вектор этих массивов и положить массив в вектор с использованием промежуточной переменой или без использования промежуточной переменной. Попробуем провернуть то же самое, что мы делали в первом видео и проверить, поможет ли здесь перемещение в случае массива, у которого нет данных в кучи. Соответственно, снова используем макрос LOGE_DURATION, сначала попробуем использовать промежуточную переменную, создаем вектор массивов. Вы на самом деле не думайте, что это какая-то странная ситуация, потому что бывает просто некоторые структуры, у которых много данных на стеке. Они прожили долгую жизнь, в них добавляли много-много-много разных полей, у них много данных на стеке, они тяжело копируются, и им тоже не поможет перемещение. На примере массива это просто продемонстрировать гораздо проще. Итак, вот у нас есть вектор массивов, мы создаем промежуточный heavy_array и записываем в него результат вызова функции make_array и затем кладем в вектор массивов с помощью метода push_back в этот самый heavy_array, и попробуем сделать то же самое без использования промежуточной переменной heavy_array. Просто вызываем push_back от make_array. И это у нас вариант без использования промежуточной переменной. Давайте попробуем запустить и посмотреть сколько будет работать каждый из вариантов, 0 миллисекунд, ну чудесно, все работает мгновенно. Я бы увеличил размер массива, он тогда потребовал бы слишком много памяти, поэтому давайте я просто добавлю этот массив в вектор не один раз, а скажем десять тысяч раз, чтобы мы увидели эффект или отсутствие эффекта от неиспользования промежуточной переменной. Итак, я напишу цикл из 10000 итераций здесь и здесь. Я буду надеяться, что 0 миллисекунд умножить на 10000, это уже не 0. Итак, запускаю код и вижу, что в одном случае код работает 1400 миллисекунду, в другом случае 1200 миллисекунд. Как будто бы есть небольшое ускорение. Давайте запустим еще раз. Запускаем код еще раз и видим, что на самом деле никакого эффекта и нет. Разница во времени работ одного варианта и другого буквально какие-то несчастные десятки миллисекунд, то есть никакого ускорения не произошло. То, о чем я вас и предупреждал, если у контейнера много данных на стеке, и данных в кучу нет или их мало, то эти все данные на стеке все равно будут копироваться, даже если вы используйте перемещения. Это первый случай, когда перемещение не помогает, есть и второй, интересный пример, давайте его тоже рассмотрим. Давайте, скажем, совсем простой пример, у нас есть функция, которая возвращает строчку, например, это строчка С++, просто какая-то маленькая строка, ну и скажем, я что-нибудь с ней такое сделаю интересное, например, я опять же сделаю вектор строк, в которой захочу складывать эти строки, я вызову функцию MakeString и положу результат в некоторую переменную s, и теперь я захочу эту строчку положить в вектор, но еще я захочу на эту строчку посмотреть. Я захочу на нее посмотреть до вызова push_back, и после. Мне интересно, повлияет ли push_back на эту строчку. Кажется в текущем коде ничего произойти не должно, у нас строчка копируется вовнутрь вектора, поэтому мы увидим С++ дважды, до push_back и после. Запускаем код и видим два раза С++, то есть все в порядке. Дальше этот код живет своей жизнью и приходит другой разработчик и думает, я же вот создал эту строчку s и дальше я ее не меняю, я просто вывожу ее и кладу в вектор. Наверное могу сделать эту переменную константной и делает эту переменную константной. Код в целом продолжает компилироваться и работает точно так же, то есть разработчик ничего не испортил, а потом приходит ещё один разработчик и говорит, у меня есть это строка, она кладется в вектор и в принципе она после вызова push_back не нужна. Допустим он случайно не увидел вот этот s out, он считает, что основная цель, это положить в вектор, и он оборачивает эту строчку в вызов функции move, добавляет модуль utility, все как положено и пробует этот код запустить. Вот что сейчас произойдёт? Произойдёт ли перемещение этой строки внутрь вектора? Нет, не произойдёт. Вы видите, что после вызова push_back от этой строки, даже будучи обернутой в move, у нас всё равно в этой строке лежит С++, то есть никакого перемещения не произошло. Почему? Потому что, если у вас есть константная переменная s и мы где-то ниже её выводим, она не может к этому моменту измениться никоим образом, она константная, и перемещение из неё тоже не сработает. Итак, поскольку перемещение меняет саму переменную, ну, оно ее опустошает как правило, забирает у неё данные, константная переменная не может переместиться, потому что он не может отдать свои данные. Даже в этом случае происходит копирование, и к этому нужно быть готовым. Итак, мы узнали два примера, в которых move семантика нам не помогает, а именно, попытка переместить объекты, у которых много данных на стеке, как правило ни к чему не приведет, потому что эти данные на стеке все равно будут копироваться, здесь вам нужен совершенно другой подход какой-то, не move семантика, это первое, и второе, что константные объекты переместить нельзя, потому что они константные, и данные у них нельзя забрать. Итак, будьте внимательны с объектами, у которых много данных на стеке и с константными объектами.