120 votes

S’il doit faire un type non mobile en C ++11 ?

J’ai été surpris que cela n’a pas apparaître dans les résultats de mes recherches, j’ai pensé que quelqu'un aurait déjà posé cette avant, compte tenu de l’utilité du déplacement sémantique en C ++11 :

Quand dois je (ou est-ce une bonne idée pour moi) qu’une classe non mobile en C ++11 ?

(Raisons autres que des problèmes de compatibilité avec le code existant, ce qui est.)

107voto

Jonathan Wakely Points 45593

Plante réponse (avant qu'il a été modifié) a donné un bon exemple d'un type qui ne devrait pas être mobile: std::mutex.

Le système d'exploitation natif de mutex type (par exemple, pthread_mutex_t sur les plate-formes POSIX) peut ne pas être d'emplacement "invariant" sens de l'objet adresse est une partie de sa valeur. Par exemple, l'OS peut garder une liste de pointeurs vers tous initialisé objets mutex. Si std::mutex contenaient un système d'exploitation natif mutex type comme un membre de données et le type natif, l'adresse doit rester fixe (parce que l'OS gère une liste de pointeurs vers ses mutex) alors std::mutex aurait pour stocker le natif de mutex type sur le tas si il serait rester au même endroit lors d'un déplacement entre std::mutex des objets ou de l' std::mutex ne doit pas se déplacer. Stocker sur le tas n'est pas possible, parce qu'un std::mutex a constexpr constructeur et doit être admissible à la constante d'initialisation (c'est à dire initialisation statique) de sorte qu'un global std::mutex est garanti pour être construit avant l'exécution du programme commence, de sorte que son constructeur ne peut pas utiliser new. Donc, la seule option qui reste est pour std::mutex immuable.

Le même raisonnement s'applique à d'autres types qui contiennent quelque chose qui nécessite une adresse fixe. Si l'adresse de la ressource doit rester fixe, ne bouge pas!

Il est un autre argument pour ne pas bouger std::mutex qui est qu'il serait très difficile de le faire en toute sécurité, parce que vous auriez besoin de savoir que personne n'est d'essayer de verrouiller le mutex au moment où il est déplacé. Depuis les mutex sont l'un des blocs de construction que vous pouvez utiliser pour protéger vos données courses, il serait regrettable qu'ils n'étaient pas à l'abri contre les courses elles-mêmes! Avec un immeuble, std::mutex vous savez les seules choses que n'importe qui peut faire une fois qu'il a été construit et avant il a été détruit, c'est pour le verrouiller et déverrouiller, et ces opérations sont expressément garanti pour être thread-safe et ne pas introduire de données courses. Ce même argument s'applique à l' std::atomic<T> objets: à moins qu'ils pourraient être déplacés de manière atomique, il ne serait pas possible de se déplacer en toute sécurité, un autre thread peut être essayer d'appeler compare_exchange_strongsur l'objet juste au moment où il est déplacé. Donc un autre cas où les types ne doivent pas être mobile est l'endroit où ils sont des éléments de base de sécurité code simultané et doit assurer l'atomicité de l'ensemble des opérations sur celles-ci. Si la valeur de l'objet peut être déplacé à un nouvel objet, à tout moment, vous devrez utiliser une variable atomique de protéger chaque variable atomique afin de savoir si il est sûr de l'utiliser ou il a été déplacé ... et une variable atomique pour protéger cette variable atomique, et ainsi de suite...

Je pense que je serais généraliser à-dire que quand un objet est juste un pur morceau de la mémoire, pas un type qui agit en tant que titulaire d'une valeur ou d'abstraction d'une valeur, il ne fait pas de sens pour le déplacer. Types fondamentaux tels que l' int ne peuvent pas se déplacer: déplacer n'est qu'une copie. Vous ne pouvez pas arracher les tripes d'un int, vous pouvez copier sa valeur et puis le mettre à zéro, mais c'est toujours un int avec une valeur, c'est juste octets de mémoire. Mais un int est toujours mobiliers dans la langue, parce que d'une copie valide opération de déplacement. Pour les non-copiable types toutefois, si vous ne voulez pas ou ne pouvez pas déplacer le morceau de la mémoire, et que vous ne pouvez pas copier de sa valeur, alors c'est non délocalisables. Un mutex ou atomique variable est un emplacement de mémoire (traités avec des propriétés spéciales) afin de ne pas faire sens pour se déplacer, et n'est pas copiable, il est donc non délocalisables.

57voto

Herb Sutter Points 1544

Réponse courte: Si un type est copiable, il devrait également être mobiles. Cependant, l'inverse n'est pas vrai: certains types, comme std::unique_ptr sont mobiles mais il n'est pas judicieux de copie; ces derniers sont naturellement déplacer uniquement les types.

Légèrement plus long suit la réponse...

Il existe deux grands types de types (entre autres plus spéciaux, tels que des traits):

  1. Valeur tels que les types, tels que l' int ou vector<widget>. Celles-ci représentent des valeurs, et devrait naturellement être copiable. En C++11, en général, vous devriez penser à déplacer comme une optimisation de la copie, et donc tous les copiable types devraient naturellement être mobile... le déplacement est juste un moyen efficace de faire une copie dans le cas le plus courant que vous n'avez pas besoin de l'objet d'origine plus et allez juste pour le détruire de toute façon.

  2. De référence tels que les types qui existent dans des hiérarchies d'héritage, telles que les classes de base et les classes virtuelles ou fonctions membres protégés. Ce sont normalement conservés par pointeur ou une référence, souvent un base* ou base&, et ainsi de ne pas fournir la copie de la construction pour éviter de découpage; si vous voulez obtenir un autre objet comme un existant, vous l'habitude de les appeler une fonction virtuelle comme clone. Ces n'avez pas besoin de déplacer de la construction ou de l'assignation pour deux raisons: Ils ne sont pas copiable, et qu'ils ont déjà encore plus efficace naturel "déplacer" opération -- vous suffit de copier/déplacer le pointeur vers l'objet et l'objet lui-même n'a pas à se déplacer vers un nouvel emplacement mémoire à tous.

La plupart des types de tomber dans l'une de ces deux catégories, mais il y a d'autres sortes de types qui sont également utiles, juste plus rares. En particulier, les types qui expriment unique de la propriété d'une ressource, comme std::unique_ptr, sont naturellement déplacer uniquement les types, parce qu'ils ne sont pas de valeur de type (il n'est pas judicieux de les copier) mais vous ne les utiliser directement (pas toujours par pointeur ou de référence) et ainsi de vouloir déplacer des objets de ce type autour d'un endroit à l'autre.

17voto

billz Points 28166

En fait quand je chercher partout, j'ai trouvé assez peu de types en C++11 ne sont pas amovibles:

  • tous mutex types(recursive_mutex , timed_mutex, recursive_timed_mutex,
  • condition_variable
  • type_info
  • error_category
  • locale::facet
  • random_device
  • seed_seq
  • ios_base
  • basic_istream<charT,traits>::sentry
  • basic_ostream<charT,traits>::sentry
  • tous atomic types
  • once_flag

Apparemment, il y a une discussion sur Clang: https://groups.google.com/forum/?fromgroups=#!topic/comp.std.c++/pCO1Qqb3Xa4

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X