Відповідь Трави (перш ніж він був відредагований) на насправді дав хороший приклад такого типу , який не повинен бути рухомим: std::mutex
.
Нативний тип мутексу ОС (наприклад, pthread_mutex_t
на платформах POSIX) може не бути "інваріантним розташуванням", тобто адреса об'єкта є частиною його значення. Наприклад, ОС може зберігати список покажчиків на всі ініціалізовані об'єкти mutex. Якщо std::mutex
в якості члена даних міститься вбудований тип мутексу ОС, а адреса основного типу повинна залишатися фіксованою (оскільки ОС підтримує список покажчиків на її мутекси), то або std::mutex
доведеться зберігати нативний тип мутексу у купі, щоб він залишався на те саме місце розташування при переміщенні між std::mutex
об'єктами або std::mutex
переміщення не повинно Збереження його на купі неможливо, тому std::mutex
що у constexpr
конструктора є конструктор і він повинен мати право на постійну ініціалізацію (тобто статичну ініціалізацію), так що глобальнийstd::mutex
гарантовано буде побудовано до запуску програми, тому її конструктор не може використовувати new
. Тож єдиний варіант, який залишився - std::mutex
це бути нерухомим.
Те ж міркування стосується інших типів, які містять щось, що вимагає фіксованої адреси. Якщо адреса ресурсу повинна залишатися фіксованою, не переміщуйте її!
Є ще один аргумент щодо не переміщення, std::mutex
який полягає в тому, що це було б дуже важко зробити це безпечно, тому що вам потрібно знати, що ніхто не намагається заблокувати мютекс у той момент, коли він переміщується. Оскільки мутекси є одним із будівельних блоків, які ви можете використовувати для запобігання перегонів даних, було б прикро, якби вони не були безпечними проти самих перегонів! З нерухомим std::mutex
ви знаєте, що єдине, що може зробити хтось після того, як він був побудований і перед його знищенням, - це заблокувати та розблокувати, і ці операції гарантовано, що вони є безпечними для потоків, і не вводять перегони даних. Цей самий аргумент застосовується і до std::atomic<T>
об'єктів: якщо б їх не вдалося перенести атомним шляхом, неможливо було б безпечно переміщувати їх, інший потік може намагатися викликатиcompare_exchange_strong
на об'єкт прямо в той момент, коли він переміщується. Отже, ще один випадок, коли типи не повинні бути рухомими, це те, що вони є низькорівневими будівельними блоками безпечного одночасного коду і повинні забезпечувати атомність усіх операцій над ними. Якщо значення об'єкта може бути переміщено до нового об'єкта в будь-який час, вам потрібно буде використовувати атомну змінну для захисту кожної атомної змінної, щоб ви знали, чи безпечно це використовувати або переміщено ... та атомну змінну для захисту що атомна змінна і так далі ...
Думаю, я б узагальнив, щоб сказати, що коли об'єкт - це просто чистий фрагмент пам'яті, а не тип, який виступає в якості власника для значення або абстрагування значення, його немає сенсу переміщувати. Основні типи, такі як int
неможливо рухатись: переміщення їх - це лише копія. Ви не можете вирвати нутрощі з int
, ви можете скопіювати його значення, а потім встановити його в нуль, але це все-таки int
значення, це лише байти пам'яті. Але int
все ще рухомиймовними термінами, оскільки копія є дійсною операцією переміщення. Однак для типів, що не підлягають копіюванню, якщо ви не хочете або не можете перемістити частину пам'яті, а також не можете скопіювати її значення, то це неможливо перемістити. Мутекс або атомна змінна - це певне місце пам’яті (оброблене спеціальними властивостями), тому не має сенсу переміщуватися, а також не підлягає копіюванню, тому воно не є рухомим.