c++ stl language-lawyer c++-standard-library

c++ - ¿Debe un contenedor STL evitar copiar elementos en sí mismo cuando el contenedor se copia en sí mismo?



language-lawyer c++-standard-library (4)

La pregunta es sobre la autoasignación. Por ejemplo, copiar un vector en sí mismo:

std::vector<std::string> vec(5, "hello"); vec = vec;

¿Debería el código anterior realizar 5 operaciones de asignación de cadenas en sí mismos, o simplemente no hacer nada? Me refiero a si la siguiente comprobación es válida:

std::vector operator=(const std::vector &rhs) { if (this == &rhs) { return *this; } ... }

Estoy trabajando en mi propia implementación de la clase std::variant (solo por diversión) y me interesa si debo agregar una comprobación de autoasignación al comienzo del operador de asignación, o ¿debería simplemente copiar el elemento contenido en sí mismo?

Entiendo que generalmente esto no importa. No debe hacer una clase que utilice el hecho de copiarse en sí misma. Pero estoy interesado si el estándar dice algo sobre esto.


¿le interesa si debo agregar un cheque de autoasignación al comienzo del operador de asignación, o simplemente debo copiar el elemento contenido en sí mismo?

C ++ Core Guidelines recomienda no hacer una verificación de autoasignación en una clase de usuario si todos sus miembros son seguros para la autoasignación:

Los operadores de Asignación de cumplimiento (simple) no deben contener el patrón if (this == &a) return *this; ???

Es por una razón de eficiencia. Las autoasignaciones son poco probables en la práctica. Son raros, por lo que es mejor evitar hacer una verificación de autoasignación en cada operación. Una verificación de autoasignación probablemente hace que el código sea más rápido en el caso de autoasignación (muy raro) y lo hace más lento en todos los demás casos (más común).

Imagina que asignas un millón de elementos. En cada operación de asignación se realiza una verificación de autoasignación. Y lo más probable es que no se haga para nada porque ninguna de las tareas es en realidad una autoasignación. Y entonces hacemos un millón de cheques inútiles.

Si omitimos hacer una verificación de autoasignación, entonces no pasa nada malo, excepto que si la autoasignación realmente sucede, entonces hacemos autoasignaciones inútiles de todos los miembros (eso a veces es más lento que hacer una sola verificación de autoasignación al comienzo de la tarea) operador). Pero si su código hace un millón de autoasignaciones, es una razón para reconsiderar su algoritmo en lugar de realizar una verificación de autoasignación en todas las operaciones.

Sin embargo, la verificación de autoasignación aún debe usarse para clases que no son seguras para la autoasignación de manera predeterminada. El ejemplo es std::vector . El vector que se está copiando primero tiene que eliminar los elementos existentes. Pero si el vector de destino y el vector de origen son el mismo objeto, al eliminar los elementos en el vector de destino también los eliminamos en el vector de origen. Por lo tanto, no será posible copiarlos después de la eliminación. Es por eso que libstdc ++ realiza una comprobación de std::vector para std::vector (aunque es posible implement std::vector sin verificación de autoasignación).

Pero no lo hace para std::variant por ejemplo. Si copia una variante en sí misma, el valor contenido se copiará en sí mismo. Ver coliru.stacked-crooked.com/a/095d633dd908d53e . Porque copiarlo en sí mismo es seguro para la autoasignación (siempre que el valor contenido sea seguro para la autoasignación).

Por lo tanto, libstdc ++ realiza una comprobación de std::vector para std::vector (para proporcionar seguridad de autoasignación) y no para std::variant (para eficiencia).


[...] si debo agregar esta comprobación al comienzo del operador de asignación [...]?

Debería, independientemente de si std::vector u otros contenedores STL lo hacen por usted. Imagine un usuario que trabaja con su biblioteca y hace x = x , fuera del alcance de los contenedores STL.

Ahora a los requisitos del contenedor STL: creo que el estándar no especifica si se requiere la asignación para realizar una verificación para ser una autoasignación (pasó por la mayoría de la sección de la Containers library de Containers library ). Esto da espacio para las optimizaciones del compilador y creo que un compilador decente debería realizar tales comprobaciones.


Las condiciones previas / posteriores de asignación de un contenedor especificado por la norma (citando el último borrador):

[tab: container.req]

r = a

Asegura: r == a.

Esto permite, pero no exige la comprobación de autoasignación.


Verificar this == &rhs es en realidad un idioma bastante conocido , y ayuda a asegurarse de que no rompa nada al garantizar que lhs y rhs son objetos diferentes. Por lo tanto, esto es válido y realmente alentado.

Sin embargo, no sé si se requieren contenedores STL para hacer la verificación.