Donc, unique_ptr peut-il être utilisé en toute sécurité dans les collections stl?

Je suis confondu avec la philosophie unique_ptr et rvalue move.

Disons que nous avons deux collections:

std::vector<std::auto_ptr> autoCollection; std::vector<std::unique_ptr> uniqueCollection; 

Maintenant, je m’attendrais à ce que les problèmes suivants échouent, car il n’y a pas d’indication de ce que fait l’algorithme en interne et peut-être de faire des copies pivot internes et autres, éliminant ainsi la propriété de auto_ptr:

 std::sort(autoCollection.begin(), autoCollection.end()); 

J’ai compris. Et le compilateur interdit à juste titre cet événement.

Mais alors je fais ceci:

 std::sort(uniqueCollection.begin(), uniqueCollection.end()); 

Et cela comstack. Et je ne comprends pas pourquoi. Je ne pensais pas que unique_ptrs pouvait être copié. Est-ce que cela signifie qu’une valeur de pivot ne peut pas être prise, alors le sorting est moins efficace? Ou est-ce que ce pivot est en fait un mouvement, qui est en fait aussi dangereux que la collection de auto_ptrs, et devrait être interdit par le compilateur?

Je pense qu’il me manque un élément d’information crucial, alors j’attends avec impatience quelqu’un pour me fournir l’aha! moment.

Je pense que c’est plus une question de philosophie que de technique 🙂

La question sous-jacente est la différence entre Move et Copy. Je ne vais pas me lancer dans le langage technique / standardiste, faisons-le simplement:

  • Copier: crée un autre object identique (ou du moins, un object qui DEVRAIT être égal)
  • Déplacer: prendre un object et le placer dans un autre endroit

Comme vous l’avez dit, il est possible de mettre en œuvre Move en termes de copie: créez une copie dans le nouvel emplacement et supprimez l’original. Cependant, il y a deux problèmes. L’un est de performance, le second concerne les objects utilisés pour RAII: lequel des deux devrait être propriétaire?

Un constructeur Move approprié résout les 2 problèmes suivants:

  • Il est clair quel object a la propriété: le nouveau, puisque l’original sera supprimé
  • Il est donc inutile de copier les ressources indiquées, ce qui permet une plus grande efficacité

Les auto_ptr et unique_ptr sont une très bonne illustration.

Avec un auto_ptr vous avez une sémantique de copie vissée: l’original et la copie ne sont pas comparables. Vous pouvez l’utiliser pour sa sémantique Move, mais vous risquez de perdre l’object pointé quelque part.

D’un autre côté, l’ unique_ptr est exactement cela: il garantit un propriétaire unique de la ressource, évitant ainsi la copie et le problème inévitable de suppression qui s’ensuivrait. Et la no-copy est également garantie à la compilation. Par conséquent, il convient aux conteneurs dans la mesure où vous n’essayez pas d’initialiser la copie.

 typedef std::unique_ptr unique_t; typedef std::vector< unique_t > vector_t; vector_t vec1; // fine vector_t vec2(5, unique_t(new Foo)); // Error (Copy) vector_t vec3(vec1.begin(), vec1.end()); // Error (Copy) vector_t vec3(make_move_iterator(vec1.begin()), make_move_iterator(vec1.end())); // Courtesy of sehe std::sort(vec1.begin(), vec1.end()); // fine, because using Move Assignment Operator std::copy(vec1.begin(), vec1.end(), std::back_inserter(vec2)); // Error (copy) 

Vous pouvez donc utiliser unique_ptr dans un conteneur (contrairement à auto_ptr ), mais un certain nombre d’opérations seront impossibles car elles impliquent une copie non prise en charge par le type.

Malheureusement, Visual Studio peut être assez laxiste dans l’application de la norme et possède également un certain nombre d’extensions que vous devrez désactiver pour garantir la portabilité du code … ne l’utilisez pas pour vérifier la norme 🙂

Les unique_ptr s sont déplacés en utilisant leur constructeur de déplacement. unique_ptr est Movable, mais pas CopyConstructable.

Il y a un excellent article sur les références rvalue ici . Si vous n’avez pas encore lu à leur sujet ou si vous êtes confus, jetez un coup d’oeil!

std::sort ne peut fonctionner qu’avec des opérations de déplacement et aucune copie, à condition qu’il n’y ait qu’une seule copie en direct de chaque object à un moment donné. Ceci est une exigence plus faible que de travailler sur place, car vous pouvez en principe allouer temporairement un autre tableau et déplacer tous les objects en les réorganisant.

Par exemple, std::vector> dépasse sa capacité, il alloue le stockage pour un vecteur plus grand, puis déplace tous les objects de l’ancien stockage vers le nouveau. Ce n’est pas une opération sur place mais c’est parfaitement valide.

En fait, les algorithmes de sorting tels que le sorting rapide et le sorting sur tas peuvent en effet fonctionner sur place sans difficulté. La routine de partition de quick-sort utilise std :: swap en interne, ce qui compte comme une opération de déplacement pour les deux objects impliqués. Lors de la sélection d’un pivot, une astuce consiste à la remplacer par le premier élément de la plage. Ainsi, elle ne sera jamais déplacée avant la fin du partitionnement.