funcion c++ c++11 swap

c++ - funcion swap en c



¿Está garantizado que std:: swap(x, x) deje x sin cambios? (1)

Esta pregunta se basa en la discusión a continuación de una publicación reciente en el blog por Scott Meyers .

Parece "obvio" que std::swap(x, x) debería dejar x sin cambios tanto en C ++ 98 como en C ++ 11, pero no puedo encontrar ninguna garantía al respecto en ninguno de los dos estándares. C ++ 98 define std::swap en términos de construcción de copia y asignación de copia, mientras que C ++ 11 lo define en términos de construcción de movimiento y asignación de movimiento, y esto parece relevante, porque en C ++ 11 (y C ++ 14), 17.6.4.9 dice que la asignación de movimientos no necesita ser autoasegurable:

Si un argumento de función se enlaza con un parámetro de referencia rvalue, la implementación puede asumir que este parámetro es una referencia única a este argumento. ... [Nota: Si un programa convierte un lvalue en un xvalue mientras pasa ese lvalue a una función de la biblioteca (por ejemplo, llamando a la función con el argumento move (x)), el programa está solicitando efectivamente esa función para tratar ese lvalue como un temporal. La implementación es libre de optimizar las comprobaciones de alias que puedan ser necesarias si el argumento fuera un valor l. "Nota final"

El informe de defectos que dio lugar a esta redacción deja clara la consecuencia:

esto aclara que los operadores de asignación de movimientos no necesitan realizar la prueba tradicional if (this! = & rhs) que se encuentra comúnmente (y es necesaria) en los operadores de asignación de copia.

Pero en C ++ 11 y C ++ 14, se espera que std::swap use esta implementación,

template<typename T> void swap(T& lhs, T& rhs) { auto temp(std::move(lhs)); lhs = std::move(rhs); rhs = std::move(temp); }

y la primera asignación es realizar una asignación a uno mismo donde el argumento es un valor de r. Si el operador de asignación de movimiento para T sigue la política de la biblioteca estándar y no se preocupa por la asignación a sí mismo, esto parecería provocar un comportamiento indefinido, y eso significaría que std::swap(x, x) tendría UB, también.

Eso es preocupante incluso de forma aislada, pero si asumimos que std::swap(x, x) se suponía que era seguro en C ++ 98, también significa que std::swap C ++ std::swap podría romper C + Código +98

Entonces, ¿está garantizado que std::swap(x, x) deje x sin cambios? En C ++ 98? ¿En C ++ 11? Si es así, ¿cómo interactúa esto con el permiso de 17.6.4.9 para que la asignación de movimientos no sea autoasignable?


Su suposición sobre la implementación de C ++ std::swap es incorrecta. En su versión, temp se está construyendo a partir de uno de los argumentos; Sin embargo, no hay necesidad de esto. temp se puede construir y el resto sigue siendo el mismo que tu ejemplo

template<typename T> void swap(T& lhs, T& rhs) // omitting noexcept specification { T temp = std::move(lhs); lhs = std::move(rhs); rhs = std::move(temp); }

La segunda declaración sigue siendo una asignación de movimiento propio, pero cualquier recurso que fuera propiedad de lhs (y rhs ) se ha movido a temp . Por lo tanto, ambos objetos deberían estar ahora en un estado "válido pero no especificado" , y la asignación no debería causar ningún problema en este punto.

Edición: Acabo de encontrar esta respuesta de Howard Hinnant, donde analiza esta situación (aproximadamente a dos terceras partes del camino hacia abajo). Su valoración es la misma que la anterior.