¿Por qué está mal usar std :: auto_ptr <> con contenedores estándar?

217

¿Por qué está mal usarlo std::auto_ptr<>con contenedores estándar?

Uhall
fuente
55
Definitivamente un +1 en esto porque he visto a mucha gente equivocarse. Es una gran pregunta para hacer.
Twokats
Lea también el artículo relacionado. Esta pregunta se considera aquí desde el otro lado. Puede ser útil para comprender más sobre los contenedores auto_ptr y STL. stackoverflow.com/questions/8630552/…
nickolay
1
movesemántica y unique_ptrfueron diseñados para evitar los problemas relacionados auto_ptr. En C ++ 03, el lenguaje no era lo suficientemente potente como para escribir una clase como auto_ptresa que se comporta de manera correcta y segura en todos los escenarios, ya que el compilador y el lenguaje no podían distinguir los valores l y r, por lo que se usaron algunos "hacks" para obtener el comportamiento deseado la mayor parte del tiempo
Phil1970
Buen artículo: STL Containers y Auto_ptrs - Por qué no se mezclan quantstart.com/articles/…
alfC

Respuestas:

124

El estándar C ++ dice que un elemento STL debe ser "construible por copia" y "asignable". En otras palabras, un elemento debe poder asignarse o copiarse y los dos elementos son lógicamente independientes. std::auto_ptrno cumple este requisito

Tome por ejemplo este código:

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.

Para superar esta limitación, se debe utilizar el std::unique_ptr, std::shared_ptro los std::weak_ptrpunteros inteligentes o los equivalentes impulso si usted no tiene C ++ 11. Aquí está la documentación de la biblioteca de impulso para estos punteros inteligentes.

Kevin
fuente
77
También debe considerar los contenedores de puntero de impulso, si no necesita una propiedad compartida.
me22
44
unique_ptrtampoco permite la copia, por lo que ciertas operaciones STL no funcionarán correctamente a menos que puedan usar su semántica de movimiento.
Mike Weller
44
"Para superar esta limitación, debe usar std::unique_ptr": esa plantilla de clase solo puede existir debido a la semántica de movimiento (su especificación requiere referencias de valor), por lo que requiere fundamentalmente C ++ 11. Sin embargo (y relacionado), el Estándar C ++ 11 ya no dice que un tipo de elemento STL debe ser "copiable-construible" y "asignable"; ser movible-construible y movible-asignable es suficiente. De hecho, las unique_ptrinstancias son solo construibles por movimiento y asignables por movimiento. ¡Pero también lo son las auto_ptrinstancias! Como consecuencia, en C ++ 11 puede hacer con auto_ptrlo que puede hacer unique_ptr.
Marc van Leeuwen
@MarcvanLeeuwen a menos que resety releasecuando sea necesario
monstruo de trinquete
2
@ratchetfreak: Hmm, no entiendo. ¿Qué? "a menos que tú resety release", no veo cómo eso se aplica a nada en mi comentario. Tenga en cuenta que ambos auto_ptry unique_ptrtienen ambos métodos, y hacen lo mismo en ambos casos.
Marc van Leeuwen
66

La semántica de copia de auto_ptrno es compatible con los contenedores.

Específicamente, copiar uno auto_ptra otro no crea dos objetos iguales ya que uno ha perdido la propiedad del puntero.

Más específicamente, copiar un auto_ptrhace que una de las copias suelte el puntero. Cuál de estos restos en el contenedor no está definido. Por lo tanto, puede perder aleatoriamente el acceso a los punteros si almacena auto_ptrsen los contenedores.

Frank Krueger
fuente
39

Dos artículos súper excelentes sobre el tema:

Lazer
fuente
Porque creo que en los casi dos años transcurridos, probablemente se ocupó del problema en cuestión.
Puppy
27
@DeadMG: sí, tienes razón. Pero ese no era mi propósito. Si alguien llega a este hilo en algún momento y quiere aprender sobre auto_ptresas cosas, estos enlaces serán útiles, estoy seguro.
Lazer
Hay muchos duplicados que son más recientes.
Cachorro
8
@DeadMG: esta pregunta no se cerró como duplicado y, por lo tanto, está abierta para la extensión. Lazer dijo lo que no se dijo antes aquí. Supongo que vino por casualidad.
Sebastian Mach
Las explicaciones en el segundo enlace, que analizan el problema después de llamar sort(), son más claras que todas las respuestas aquí.
Chaosink
17

Los contenedores STL deben poder copiar los artículos que almacena en ellos, y están diseñados para esperar que el original y la copia sean equivalentes. Los objetos de puntero automático tienen un contrato completamente diferente, por lo que la copia crea una transferencia de propiedad. Esto significa que los contenedores de auto_ptr exhibirán un comportamiento extraño, dependiendo del uso.

Hay una descripción detallada de lo que puede salir mal en el artículo 8 de Effective STL (Scott Meyers) y también una descripción no tan detallada en el artículo 13 de Effective C ++ (Scott Meyers).

Garth Gilmour
fuente
12

Los contenedores STL almacenan copias de artículos contenidos. Cuando se copia un auto_ptr, establece el antiguo ptr en nulo. Muchos métodos de contenedor se rompen por este comportamiento.

Dustin Getz
fuente
Pero, cuando usas unique_ptr, obtienes casi lo mismo, ya que solo un unique_ptr puede tener la propiedad del objeto.
Trazador
2
@Tracer, unique_ptrcomo cualquier objeto C ++ 11 apropiado, solo puede transferir la propiedad de su recurso cuando se construye o se asigna el movimiento, asegurando que el programador debe pasar deliberadamente std::move(sourceObject)un valor temporal o temporal, en lugar de pasar un valor l y de manera no intuitiva / impredecible tenerlo mutado por la asignación de copias ... que, como se enfatizó a fondo aquí, era un problema central de auto_ptr.
underscore_d
4

El estándar C ++ 03 (ISO-IEC 14882-2003) dice en la cláusula 20.4.5, párrafo 3:

[...] [ Nota: [...] auto_ptr no cumple con los requisitos de CopyConstructible y Asignable para elementos del contenedor de la Biblioteca estándar y, por lo tanto, crear una instancia de un contenedor de la Biblioteca estándar con un auto_ptr da como resultado un comportamiento indefinido. - nota final ]

El estándar C ++ 11 (ISO-IEC 14882-2011) dice en el apéndice D.10.1 párrafo 3:

[...] Nota: [...] las instancias de auto_ptr cumplen los requisitos de MoveConstructible y MoveAssignable, pero no cumplen los requisitos de CopyConstructible y CopyAssignable. - nota final]

El estándar C ++ 14 (ISO-IEC 14882-2014) dice en el apéndice C.4.2 Anexo D: características de compatibilidad:

Cambio : las plantillas de clase auto_ptr, unary_function y binary_function, las plantillas de función random_shuffle y las plantillas de función (y sus tipos de retorno) ptr_fun, mem_fun, mem_fun_ref, bind1st y bind2nd no están definidas.
Justificación : Reemplazada por nuevas características.
Efecto sobre la característica original : el código válido de C ++ 2014 que usa estas plantillas de clase y plantillas de función puede fallar al compilarse en esta Norma Internacional.

bitek
fuente