Стандартная библиотечная политика о назначении перемещения реализация позволяет предположить, что самостоятельное назначение никогда не произойдет; это кажется мне действительно плохой идеей, учитывая, что:
remove_if
семье нужно позаботиться об этом угловом деле;Итак, почему такое решение?
In Особенно в коде библиотеки, где разработчики могут свободно использовать специфичные для компилятора подсказки о «ожидаемом результате ветвления» (подумайте __builtin_expect
в gcc /__assume
в VC ++).
Перемещено из объектов в std
должны быть отброшены или назначены до повторного использования. Что-нибудь это не полностью бесплатно, кроме того, что не обещано.
Иногда вещи бесплатны. Как контейнер, созданный при перемещении, пуст. Обратите внимание, что в некоторых случаях, связанных с перемещением, такой гарантии нет, поскольку некоторые реализации могут предпочесть перемещать элементы вместо буферов. Почему разница? Один был бесплатной дополнительной гарантией, другой нет.
Филиал или другой чек не является полностью бесплатным. Он занимает интервал предсказания ветвления, и даже если предсказание является просто почти бесплатным.
Более того, a = std::move(a);
является доказательством логической ошибки. Назначение: от a
(в std
) означает, что вы будете только назначать или отбрасывать a
, И все же здесь вы хотите, чтобы на следующей строке было определенное состояние. Либо вы знаете, что вы назначаете себя, либо нет. Если вы этого не сделаете, то теперь вы двигаетесь от объекта, который вы также населяете, и вы этого не знаете.
Принцип «делай мелочи, чтобы обеспечить безопасность» вступает в противоречие с «ты не платишь за то, что не используешь». В этом случае победил второй.
Якк дает очень хороший ответ (как обычно и голосовал), но на этот раз я хотел добавить немного больше информации.
Политика в отношении самостоятельных заданий изменилась за последние полвека. Мы только недавно выяснили этот угловой случай в LWG 2468. На самом деле, я должен быть более точным: неформальная группа между заседаниями согласилась с решением этого вопроса, и, скорее всего, она будет проголосована за рабочий проект C ++ 1z в следующем месяце (ноябрь 2016 г.).
Суть вопроса заключается в том, чтобы изменить MoveAssignable
требования прояснить, что если целью и источником назначения перемещения является один и тот же объект, то нет никаких требований к значению объекта после назначения (за исключением того, что оно должно быть действительным состоянием). Далее поясняется, что если этот объект используется с std :: lib, он все равно должен соответствовать требованиям алгоритма (например, LessThanComparable
) так или иначе это было назначено движение или даже само назначение движения.
Так…
T x, y;
x = std::move(y); // The value of y is unspecified and x == the old y
x = std::move(x); // The value of x is unspecified
Но оба x
а также y
все еще в действительных состояниях. Память не просочилась. Неопределенного поведения не произошло.
Обоснование этой позиции
Это все еще производительность. Однако признается, что swap(x, x)
был законным с C ++ 98 и происходит в дикой природе. Кроме того, начиная с C ++ 11 swap(x, x)
выполняет самостоятельное задание x
:
T temp = std::move(x);
x = std::move(x);
x = std::move(temp);
До C ++ 11, swap(x, x)
был (довольно дорогой) неоперативный (с использованием копирования вместо перемещения). LWG 2468 разъясняет, что с C ++ 11 и после, swap(x, x)
по-прежнему (не так дорого) не работает (с использованием перемещения вместо копирования).
Подробности:
T temp = std::move(x);
// temp now has the value of the original x, and x's value is unspecified
x = std::move(x);
// x's value is still unspecified
x = std::move(temp);
// x's value now has the value of temp, which is also the original x value
Чтобы выполнить этот запрет, самостоятельное задание на x
может делать все, что захочет, пока он уходит x
в действительном состоянии, не утверждая и не выбрасывая исключение.
Если вы хотите указать, что для вашего типа T
само-перемещение-назначение — это запрет, это прекрасно. Std :: lib делает именно это для unique_ptr
,
Если вы хотите указать, что для вашего типа U
self-move-assignment оставляет его в допустимом, но неопределенном состоянии, что тоже хорошо. Std :: lib делает именно это для vector
, Некоторые реализации (я полагаю, VS) идут на труд, чтобы сделать самостоятельное перемещение-назначение на vector
нет оп. Другие этого не делают (например, libc ++).