Сделайте переместительное присваивание безопасным для самоприсваивания
Причина
Если x = x изменяет значение x, люди будут удивлены и могут произойти серьёзные ошибки. Однако люди обычно не пишут прямо самоприсваивание, которое превращается в перемещение, но это может произойти. Однако std::swap реализуется с использованием операций перемещения, поэтому если вы случайно выполните swap(a, b), где a и b относятся к одному и тому же объекту, отсутствие обработки самоперемещения может быть серьёзной и тонкой ошибкой.
Пример
class Foo {
string s;
int i;
public:
Foo& operator=(Foo&& a) noexcept;
// ...
};
Foo& Foo::operator=(Foo&& a) noexcept // OK, но есть стоимость
{
if (this == &a) return *this; // эта строка является избыточной
s = std::move(a.s);
i = a.i;
return *this;
}
Аргумент один на миллион против тестов if (this == &a) return *this; из обсуждения самоприсваивания ещё более применим к самоперемещению.
Примечание
Нет известного общего способа избежать теста if (this == &a) return *this; для переместительного присваивания и при этом получить правильный результат (т.е. после x = x значение x остаётся неизменным).
Примечание
Стандарт ISO гарантирует только "действительное, но неопределённое" состояние для контейнеров стандартной библиотеки. Видимо, это не было проблемой в течение примерно 10 лет экспериментального и производственного использования. Пожалуйста, свяжитесь с редакторами, если вы найдёте контрпример. Правило здесь более осторожно и настаивает на полной безопасности.
Пример
Вот способ переместить указатель без теста (представьте это как код в реализации переместительного присваивания):
// переместить из other.ptr в this->ptr
T* temp = other.ptr;
other.ptr = nullptr;
delete ptr; // при самоперемещении this->ptr также null; delete - это операция без действия
ptr = temp; // при самоперемещении исходный ptr восстанавливается
Применение
- (Умеренное) В случае самоприсваивания оператор переместительного присваивания не должен оставлять объект с элементами-указателями, которые были
deleteны или установлены вnullptr. - (Не применимо) Посмотрите на использование типов контейнеров стандартной библиотеки (включая
string) и считайте их безопасными для обычного (не критичного для жизни) использования.