¿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?


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.


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.


39

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


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).


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.


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.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.