Pourquoi est-ce mal d'utiliser std :: auto_ptr <> avec des conteneurs standard?

217

Pourquoi est-ce mal d'utiliser std::auto_ptr<>avec des conteneurs standard?

Uhall
la source
5
Certainement un +1 à ce sujet car j'ai vu tant de gens se tromper. C'est une excellente question à poser.
twokats
Veuillez également lire l'article connexe. Cette question est considérée ici de l'autre côté. Peut être utile pour en savoir plus sur les conteneurs auto_ptr et STL. stackoverflow.com/questions/8630552/…
nickolay
1
movesémantique et unique_ptront été conçus pour éviter les problèmes liés à auto_ptr. En C ++ 03, le langage n'était pas assez puissant pour écrire une classe comme auto_ptrcelle-ci se comportait correctement et en toute sécurité dans tous les scénarios car le compilateur et le langage n'étaient pas en mesure de distinguer les valeurs l et r, donc certains "hacks" ont été utilisés pour obtenir le comportement souhaité le plus souvent.
Phil1970
Bel article: Conteneurs STL et Auto_ptrs - Pourquoi ils ne se mélangent
alfC

Réponses:

124

La norme C ++ dit qu'un élément STL doit être "constructible par copie" et "attribuable". En d'autres termes, un élément doit pouvoir être affecté ou copié et les deux éléments sont logiquement indépendants. std::auto_ptrne remplit pas cette condition.

Prenons par exemple ce code:

class X
{
};

std::vector<std::auto_ptr<X> > vecX;
vecX.push_back(new X);

std::auto_ptr<X> pX = vecX[0];  // vecX[0] is assigned NULL.

Pour surmonter cette limitation, vous devez utiliser les pointeurs std::unique_ptr, std::shared_ptrou std::weak_ptrsmart ou les équivalents boost si vous n'avez pas C ++ 11. Voici la documentation de la bibliothèque boost pour ces pointeurs intelligents.

Kevin
la source
7
Vous devriez également prendre en compte les conteneurs de pointeurs de suralimentation, si vous n'avez pas besoin d'une propriété partagée.
me22
4
unique_ptrinterdit également la copie, de sorte que certaines opérations STL ne fonctionneront pas correctement à moins qu'elles ne puissent utiliser sa sémantique de déplacement.
Mike Weller
4
"Pour surmonter cette limitation, vous devez utiliser le std::unique_ptr": ce modèle de classe ne peut exister qu'en raison de la sémantique de déplacement (sa spécification nécessite des références rvalue), il requiert donc fondamentalement C ++ 11. Cependant (et connexe) la norme C ++ 11 ne dit plus qu'un type d'élément STL doit être "constructible par copie" et "assignable"; être mobile constructible et assignable au mouvement suffit. En effet, les unique_ptrinstances ne sont constructibles et assignables que par déplacement. Mais les auto_ptrinstances aussi! Par conséquent, en C ++ 11, vous pouvez faire avec auto_ptrce que vous pouvez faire unique_ptr.
Marc van Leeuwen
@MarcvanLeeuwen sauf si vous resetet releaseau besoin
ratchet freak
2
@ratchetfreak: Hmm, je ne comprends pas. Quoi? "sauf si vous resetet release", je ne vois pas comment cela s'applique à quoi que ce soit dans mon commentaire. Notez que les deux auto_ptret unique_ptront ces deux méthodes, et ils font la même chose dans les deux cas.
Marc van Leeuwen
66

La sémantique de copie de auto_ptrn'est pas compatible avec les conteneurs.

Plus précisément, la copie de l'un auto_ptrà l'autre ne crée pas deux objets égaux, car l'un a perdu sa propriété du pointeur.

Plus précisément, la copie d'un auto_ptrfait lâcher l'une des copies du pointeur. Lequel de ceux-ci reste dans le conteneur n'est pas défini. Par conséquent, vous pouvez perdre aléatoirement l'accès aux pointeurs si vous stockez auto_ptrsdans les conteneurs.

Frank Krueger
la source
39

Deux excellents articles sur le sujet:

Lazer
la source
Parce que je pense qu'au cours des deux années qui ont suivi, il a probablement réglé le problème en question.
Puppy
27
@DeadMG: oui, vous avez raison. Mais ce n'était pas mon but. Si quelqu'un vient sur ce fil un jour et veut en savoir plus auto_ptr, ces liens seront utiles, j'en suis sûr.
Lazer
Il y a beaucoup de doublons plus récents.
Puppy
8
@DeadMG: Cette question n'a pas été fermée en tant que doublon et est donc ouverte pour une extension. Lazer a dit ce qui n'avait pas été dit auparavant. Je suppose qu'il est venu par hasard.
Sebastian Mach
Les explications du deuxième lien, qui analysent le problème après avoir appelé sort(), sont plus claires que toutes les réponses ici.
chaosink
17

Les conteneurs STL doivent pouvoir copier les éléments que vous y stockez et sont conçus pour s'attendre à ce que l'original et la copie soient équivalents. Les objets de pointeur automatique ont un contrat complètement différent, où la copie crée un transfert de propriété. Cela signifie que les conteneurs de auto_ptr présenteront un comportement étrange, selon l'utilisation.

Il y a une description détaillée de ce qui peut mal tourner dans l'article 8 de la STL efficace (Scott Meyers) et également une description pas si détaillée dans l'article 13 du C ++ efficace (Scott Meyers).

Garth Gilmour
la source
12

Les conteneurs STL stockent des copies des éléments contenus. Lorsqu'un auto_ptr est copié, il définit l'ancien ptr sur null. De nombreuses méthodes de conteneur sont rompues par ce comportement.

Dustin Getz
la source
Mais, lorsque vous utilisez unique_ptr, vous obtenez à peu près la même chose, car un seul unique_ptr peut avoir la propriété de l'objet?
Tracer
2
@Tracer, unique_ptrcomme tout objet C ++ 11 approprié, ne peut transférer la propriété de sa ressource que lors de la construction ou de l'attribution d'un mouvement, garantissant que le programmeur doit délibérément passer un std::move(sourceObject)ou un temporaire, plutôt que de passer une valeur l et de le faire muter de manière non intuitive / imprévisible. la copie-cession ... qui, comme souligné ici, était un problème central de auto_ptr.
underscore_d
4

La norme C ++ 03 (ISO-IEC 14882-2003) stipule au paragraphe 3 du paragraphe 20.4.5:

[...] [ Remarque: [...] auto_ptr ne répond pas aux exigences CopyConstructible et Assignable pour les éléments de conteneur de bibliothèque standard et donc instancier un conteneur de bibliothèque standard avec un auto_ptr entraîne un comportement indéfini. - note de fin ]

La norme C ++ 11 (ISO-IEC 14882-2011) dit dans l'annexe D.10.1 paragraphe 3:

[...] Remarque: [...] Les instances de auto_ptr répondent aux exigences de MoveConstructible et MoveAssignable, mais ne répondent pas aux exigences de CopyConstructible et CopyAssignable. - note de fin]

La norme C ++ 14 (ISO-IEC 14882-2014) dit dans l'appendice C.4.2 Annexe D: caractéristiques de compatibilité:

Modification : les modèles de classe auto_ptr, unary_function et binary_function, les modèles de fonction random_shuffle et les modèles de fonction (et leurs types de retour) ptr_fun, mem_fun, mem_fun_ref, bind1st et bind2nd ne sont pas définis.
Justification : Remplacé par de nouvelles fonctionnalités.
Effet sur la fonctionnalité d'origine : le code C ++ 2014 valide qui utilise ces modèles de classe et modèles de fonction peut ne pas être compilé dans la présente Norme internationale.

bitek
la source