Prosche wrote: ↑25 Jan 2018 03:03
AndreyT wrote: ↑25 Jan 2018 02:45
А если подумать головой, то для объектов, владеющих ресурсами, из соображений эффективности разумнее по возможности реализовывать move через swap - так чтобы moved-from объект в случае присваивания в него мог воспользоваться уже имеющимися ресурсами, вместо того, чтобы выделять с нуля новые.
Что за бред?
Вам стоит воспользоваться вашим же советом про голову. Во-первых, с какой стати кто-то передавая объект по && будет ожидать, что в нем появятся какие-то ресурсы.
Что за чушь вы несете? Остались ли/появились ли в moved from объекте ресурсы или нет - НЕ является информацией, которая каким-то образом видна снаружи. Поэтому откуда вы взяли, что кто-то будет что-то "ожидать" - мне не ясно. Такого здесь никто не говорил - это вы сами придумали. Снаружи об этом никто ничего не знает и знать не должен. Для наружных наблюдателей moved from объект находится в некотором условно валидном состоянии, которому, например, можно присваивать - не более. Осталось ли у него что-то внутри - никого снаружи не касается.
Знание о том, сохранились ли/появились ли в объекте ресурсы - это исключительно
скрытое внутреннее знание объекта и личное дело объекта. Если объект после move будет деструктирован, то он вместе с собой уничтожит и эти ресурсы. Если в объект будет выполнено присваивание, то он тогда проверит возможность переиспользования имеющихся ресурсов вместо выделения новых. Вот ради этого мы эти ресурсы и хранили (!).
На этом и основана идиома реализации move через swap между объектами-участниками. Для вас это новость? Как говорится, "век живи - век учись". Этой идиомой не надо злоупотреблять, ибо если возможности/необходимости вышеупомянутого переиспользования ресурсов нет, то swap будет менее эффективным подходом. Но тем не мнее...
Prosche wrote: ↑25 Jan 2018 03:03Свап применяют когда известно что объект будет разрушен и делают это по причине атомарности ака безопасности процедуры свапа, перед копированием.
Это утверждение звучит как что-то из высказываний армейских прапорщиков. "Молоток применяют для подбивания каблуков, а не для того, чтобы по танку им стучать". Из того, что вы видели применение swap только в copy-and-swap идиоме совсем не следует, что swap больше нигде применять нельзя.
А вот откуда вы взяли "атомарность" swap вообще не ясно. В copy-and-swap идиоме swap применяют из-за того, что он не бросает исключений. Никакой "атомарности" у него нет в принципе.
Prosche wrote: ↑25 Jan 2018 03:03Во-вторых, раз уж вы заговорили о быстродействии, то опять же подумайте, что быстрее, дефолт конструктор + свап или ассайнмент поинтера ресурсов и обнуление поинтера отдающего.
Вот поэтому я и сказал - включать мозги.
Давайте на примере `std::vector<int> a, b;` рассмотрим пример перемещающей операции присваивания `a = std::move(b);`. (Заранее скажу, что я не буду следить за exception safety, ибо речь не о том. Все эти способы допускают exception safe реализации без изменения их относительных эффективностей.)
---
Сначала для случая, когда объект `b` затем сразу деструктируется:
Обычный move с немедленным освобождением ресурсов
Move-присваивание:
1. Освобождаем память `a`
2. Копируем поля из `b` в `a`, обнуляем поля `b`
Move-присваивание закончилось
3. Деструктируем `b` (почти ничего не происходит, ибо `b` "пуст")
Мove через swap
Move-присваивание:
1. Oбмениваем местами поля `a` и `b`
Move-присваивание закончилось
2. Деструктируем `b` (при этом освобождается память, пришедшая в `b` из `a`)
Никакой разницы в эффективности пары "move + деструкция" здесь нет и в помине. Только во втором случае прежние ресурсы `a` переходят к `b` и живут в общем случае дольше.
---
А теперь рассмотрим ситуацию, когда `b` не деструктируется, а переиспользуется: в него copy-присваивается новое значение, т.е. `a = std::move(b); b = c;`
Обычный move с немедленным освобождением ресурсов
Move-присваивание:
1. Освобождаем память `a`
2. Копируем поля из `b` в `a`, обнуляем поля `b`
Move-присваивание закончилось
Copy-присваивание:
4. Выделяем новую память в `b`
5. Копируем данные из `c` в `b`
Copy-присваивание закончилось
Мove через swap
Move-присваивание:
1. Oбмениваем местами поля `a` и `b`
Move-присваивание закончилось
Copy-присваивание:
2. Проверяем, достаточно ли capacity в `b` для хранения `c`. Если достаточно, то просто копируем данные. Все.
3. Иначе, освобождаем память `b`
4. Выделяем новую память в `b`
5. Копируем данные из `c` в `b`
Copy-присваивание закончилось
Опять, эффективность получилась одинаковой за исключением случая "удачи" на шаге 2 в варианте "move через swap", в каковом случае этот вариант будет
намного эффективнее. Вот ради этой возможной удачи мы и не спешим освобождать ресурсы, а позволяем им еще немножко пожить в `b`. Вдруг пригодятся?
---
Так понятнее?
Тонкостью в этом случае является ситуация `std::vector<SomeComplicatedClass>` и момент деструкции элементов вектора имеет для программы какое-то важное значение, т.е. задержка деструкции в этом случае будет нежелательной. Но на само деле потенциальная эффективность варианта "Мove через swap" держится лишь на экономии на тяжелых операциях перераспределения сырой памяти. Деструкцию элементов массива в обоих случаях можно делать сразу, т.е. этот момент - легко решаем.