La réponse de Herb (avant modification) a fait un bon exemple d'un type qui ne doit pas être mobile: std::mutex.
Le type mutex natif du système d'exploitation (par exemple pthread_mutex_tsur les plates-formes POSIX) peut ne pas être "invariant d'emplacement", ce qui signifie que l'adresse de l'objet fait partie de sa valeur. Par exemple, le système d'exploitation peut conserver une liste de pointeurs vers tous les objets mutex initialisés. S'il std::mutexcontenait un type mutex du système d'exploitation natif en tant que membre de données et que l'adresse du type natif doit rester fixe (car le système d'exploitation maintient une liste de pointeurs vers ses mutex), l'un ou l'autre std::mutexdevrait stocker le type mutex natif sur le tas afin qu'il reste à au même endroit lorsqu'il est déplacé entre des std::mutexobjets ou std::mutexne doit pas bouger. Le stocker sur le tas n'est pas possible, car a std::mutexa un constexprconstructeur et doit être éligible pour une initialisation constante (ie initialisation statique) de sorte qu'un globalstd::mutexest garanti pour être construit avant le début de l'exécution du programme, donc son constructeur ne peut pas utiliser new. Donc, la seule option qui reste est std::mutexd'être inamovible.
Le même raisonnement s'applique aux autres types qui contiennent quelque chose qui nécessite une adresse fixe. Si l'adresse de la ressource doit rester fixe, ne la déplacez pas!
Il y a un autre argument pour ne pas bouger, std::mutexc'est qu'il serait très difficile de le faire en toute sécurité, car vous auriez besoin de savoir que personne n'essaye de verrouiller le mutex au moment où il est déplacé. Étant donné que les mutex sont l'un des éléments de base que vous pouvez utiliser pour empêcher les courses de données, il serait malheureux qu'ils ne soient pas en sécurité contre les races elles-mêmes! Avec un immeuble, std::mutexvous savez que la seule chose que tout le monde peut faire une fois qu'il a été construit et avant qu'il ne soit détruit est de le verrouiller et de le déverrouiller, et ces opérations sont explicitement garanties pour être thread-safe et ne pas introduire de courses de données. Ce même argument s'applique aux std::atomic<T>objets: à moins qu'ils ne puissent être déplacés de manière atomique, il ne serait pas possible de les déplacer en toute sécurité, un autre thread pourrait essayer d'appelercompare_exchange_strongsur l'objet juste au moment où il est déplacé. Donc, un autre cas où les types ne devraient pas être déplaçables est celui où ils sont des blocs de construction de bas niveau d'un code concurrent sûr et doivent garantir l'atomicité de toutes les opérations sur eux. Si la valeur de l'objet peut être déplacée vers un nouvel objet à tout moment, vous devez utiliser une variable atomique pour protéger chaque variable atomique afin que vous sachiez s'il est sûr de l'utiliser ou si elle a été déplacée ... et une variable atomique à protéger cette variable atomique, et ainsi de suite ...
Je pense que je généraliserais pour dire que lorsqu'un objet n'est qu'un pur morceau de mémoire, pas un type qui agit comme un support pour une valeur ou une abstraction d'une valeur, cela n'a pas de sens de le déplacer. Les types fondamentaux tels que intne peuvent pas bouger: les déplacer n'est qu'une copie. Vous ne pouvez pas extraire les tripes d'un int, vous pouvez copier sa valeur puis la mettre à zéro, mais c'est toujours un intavec une valeur, ce ne sont que des octets de mémoire. Mais un intest toujours mobiledans les termes de la langue car une copie est une opération de déplacement valide. Cependant, pour les types non copiables, si vous ne voulez pas ou ne pouvez pas déplacer la partie de mémoire et que vous ne pouvez pas non plus copier sa valeur, alors elle n'est pas déplaçable. Un mutex ou une variable atomique est un emplacement spécifique de la mémoire (traité avec des propriétés spéciales) donc n'a pas de sens de se déplacer, et n'est pas non plus copiable, donc il n'est pas déplaçable.