Все ли реализации std::move(), основанные на std::swap(), содержат ошибки?
Возможный дубликат:
Что я могу сделать с перемещенным объектом?
Например, посмотрите этот код:
template<class T>
void swap(T& a, T& b)
{
T tmp(std::move(a));
a = std::move(b);
b = std::move(tmp);
}
Это только у меня, или здесь есть ошибка? если ты move
a
в tmp
то не a
стать недействительным?
т.е. не должен ли двигаться-назначение a
от b
быть вызовом move-constructor с размещением new
вместо?
Если нет, то в чем разница между конструктором перемещения и оператором присваивания перемещения?
template<class T>
void swap(T& a, T& b)
{
T tmp(std::move(a));
new(&a) T(std::move(b));
new(&b) T(std::move(tmp));
}
3 ответа
Когда вы перемещаете данные из объекта, предполагаемая семантика заключается в том, что объект, из которого был перемещен, оказывается в неопределенном, но допустимом состоянии. Это означает, что вы не можете ничего предсказать, в каком состоянии находится объект, кроме того, что это будет правильно сформированный объект. Это не совсем то же самое, что "этот объект мертв и ушел". Перемещение данных из объекта не заканчивает время жизни объекта - оно просто меняет его состояние на что-то неопределенное - и, следовательно, совершенно безопасно назначить этому объекту новое значение.
В результате начальная версия функции подкачки безопасна. После перемещения данных из объекта a этот объект содержит некоторое неопределенное "безопасное, но непредсказуемое" значение. Это значение затем перезаписывается при присвоении ему значения b.
Эта вторая версия небезопасна, потому что время жизни a не закончилось до того, как вы попытаетесь создать новый объект поверх него. Это приводит к неопределенному поведению.
Надеюсь это поможет!
a
все еще будет в силе. Данные в a
не будет более надежным, хотя. Вы не освобождаете a
когда вы перемещаете его ресурсы, поэтому совершенно нормально перемещать новый ресурс в a
,
Перемещение не делает объект недействительным. Скорее, он остается в действительном, но неопределенном состоянии в целом. У определенных классов есть дополнительные гарантии; например, std::unique_ptr
гарантирует, что он будет нулевым после перемещения из.
Сохранение действительного объекта, в частности, означает, что его вполне можно назначить объекту, что и делает исходный код.
Ваше собственное предлагаемое решение сильно нарушено: когда вы размещаете новый объект поверх старого, время жизни старого объекта заканчивается. Однако, если деструктор класса имеет эффекты, то исключение вызова деструктора является неопределенным поведением.
Более того, если вы сначала правильно вызвали деструктор, но потом обнаружили исключение в конструкторе, у вас возникнут проблемы, поскольку у вас нет действительного объекта, который нужно уничтожить при выходе из области видимости. Вот мой связанный вопрос по этой теме.