Ответ Травы (прежде чем он был отредактирован) на самом деле дал хороший пример такого типа , который не должен быть подвижным: std::mutex
.
Собственный тип мьютекса ОС (например, pthread_mutex_t
на платформах POSIX) может не быть «инвариантным относительно местоположения», что означает, что адрес объекта является частью его значения. Например, ОС может хранить список указателей на все инициализированные объекты мьютекса. Если std::mutex
в качестве члена данных содержится собственный тип мьютекса ОС, а адрес собственного типа должен оставаться фиксированным (поскольку ОС поддерживает список указателей на свои мьютексы), то любой из std::mutex
них должен будет сохранить собственный тип мьютекса в куче, чтобы он оставался в одно и то же место при перемещении между std::mutex
объектами или объект std::mutex
не должен перемещаться. Сохранение его в куче невозможно, потому что у a std::mutex
есть constexpr
конструктор и он должен иметь право на постоянную инициализацию (т.е. статическую инициализацию), чтобы глобальныйstd::mutex
гарантированно создается до начала выполнения программы, поэтому его конструктор не может использовать new
. Так что остается единственный вариант - std::mutex
оставаться неподвижным.
То же самое относится и к другим типам, которые содержат что-то, для чего требуется фиксированный адрес. Если адрес ресурса должен оставаться неизменным, не перемещайте его!
Есть еще один аргумент в пользу отказа от перемещения, std::mutex
который заключается в том, что было бы очень сложно сделать это безопасно, потому что вам нужно знать, что никто не пытается заблокировать мьютекс в момент его перемещения. Поскольку мьютексы являются одним из строительных блоков, которые вы можете использовать для предотвращения гонок данных, было бы прискорбно, если бы они были небезопасны против самих гонок! С неподвижным объектом std::mutex
вы знаете, что единственное, что каждый может сделать с ним после того, как он был построен и до того, как он был уничтожен, - это заблокировать и разблокировать его, и эти операции явно гарантируются как потокобезопасные и не вызывают гонок данных. Тот же аргумент применяется к std::atomic<T>
объектам: если они не могут быть перемещены атомарно, их невозможно будет безопасно перемещать, другой поток может пытаться вызватьcompare_exchange_strong
на объекте прямо в момент его перемещения. Таким образом, другой случай, когда типы не должны быть подвижными, - это когда они являются низкоуровневыми строительными блоками безопасного параллельного кода и должны обеспечивать атомарность всех операций с ними. Если значение объекта может быть перемещено в новый объект в любое время, вам нужно будет использовать атомарную переменную для защиты каждой атомарной переменной, чтобы вы знали, безопасно ли ее использовать или она была перемещена ... и атомарную переменную для защиты эта атомарная переменная и так далее ...
Я думаю, что обобщил бы, сказав, что когда объект - это просто чистый фрагмент памяти, а не тип, который действует как держатель для значения или абстракции значения, нет смысла перемещать его. Основные типы, например, int
не могут двигаться: их перемещение - это просто копия. Вы не можете вырвать кишки из a int
, вы можете скопировать его значение, а затем установить его на ноль, но он все еще имеет int
значение, это просто байты памяти. Но int
все еще подвиженв терминах языка, потому что копия является допустимой операцией перемещения. Однако для некопируемых типов, если вы не хотите или не можете перемещать фрагмент памяти, а также не можете скопировать его значение, он не может быть перемещен. Мьютекс или атомарная переменная - это определенное место в памяти (обрабатываемое особыми свойствами), поэтому не имеет смысла перемещать, а также не подлежит копированию, поэтому его нельзя перемещать.