std::swap()es utilizado por muchos contenedores estándar (como std::listy std::vector) durante la clasificación e incluso la asignación.
Pero la implementación estándar de swap()es muy generalizada y bastante ineficiente para los tipos personalizados.
Por lo tanto, se puede ganar eficiencia sobrecargando std::swap()con una implementación específica de tipo personalizado. Pero, ¿cómo puede implementarlo para que sea utilizado por los contenedores estándar?

Respuestas:
La forma correcta de sobrecargar el intercambio es escribirlo en el mismo espacio de nombres que lo que está intercambiando, para que se pueda encontrar mediante la búsqueda dependiente de argumentos (ADL) . Una cosa particularmente fácil de hacer es:
fuente
std::sortque usa ADL para intercambiar elementos es C ++ 03 no conforme pero conforme a C ++ 11. Además, ¿por qué -1 una respuesta basada en el hecho de que los clientes pueden usar código no idiomático?Atención Mozza314
Aquí hay una simulación de los efectos de una
std::algorithmllamada genéricastd::swap, y el usuario proporciona su intercambio en el espacio de nombres std. Como se trata de un experimento, esta simulación utiliza ennamespace explugar denamespace std.Para mí esto imprime:
Si su compilador imprime algo diferente, entonces no está implementando correctamente la "búsqueda en dos fases" para las plantillas.
Si su compilador es conforme (a cualquiera de C ++ 98/03/11), entonces dará el mismo resultado que muestro. Y en ese caso, sucede exactamente lo que temes que suceda. Y poner su
swapen el espacio de nombresstd(exp) no impidió que sucediera.Dave y yo somos miembros del comité y hemos trabajado en esta área de la norma durante una década (y no siempre de acuerdo entre nosotros). Pero este tema se ha resuelto durante mucho tiempo y ambos estamos de acuerdo en cómo se ha resuelto. Ignore la opinión / respuesta experta de Dave en esta área bajo su propio riesgo.
Este problema salió a la luz después de la publicación de C ++ 98. Aproximadamente en 2001, Dave y yo comenzamos a trabajar en esta área . Y esta es la solución moderna:
La salida es:
Actualizar
Se ha hecho una observación que:
¡trabajos! Entonces, ¿por qué no usar eso?
Considere el caso de que su
Aes una plantilla de clase:Ahora no vuelve a funcionar. :-(
Entonces podría poner
swapen el espacio de nombres std y hacer que funcione. Sin embargo, usted tiene que recordar para ponerswapenAespacio de nombres 's para el caso cuando se tiene una plantilla:A<T>. Y dado que ambos casos funcionarán si ponesswapenAel espacio de nombres, es más fácil recordar (y enseñar a otros) que lo hagan de una manera.fuente
template <>su primer ejemplo, obtengo una salidaexp::swap(A, A)de gcc. Entonces, ¿por qué no preferir la especialización?using std::swapes una excepción a la regla de "nunca poner usando instrucciones dentro de los archivos de encabezado"? De hecho, ¿por qué no ponerlousing std::swapdentro<algorithm>? Supongo que podría romper una pequeña minoría del código de las personas. ¿Quizás desaprobar el apoyo y eventualmente ponerlo?using std::swapalcance de la función dentro de sus encabezados. Sí,swapes casi una palabra clave. Pero no, no es una palabra clave. Así que es mejor no exportarlo a todos los espacios de nombres hasta que realmente sea necesario.swapes muy parecidooperator==. La mayor diferencia es que nadie piensa en llamaroperator==con una sintaxis de espacio de nombres calificada (sería demasiado feo).No está permitido (según el estándar C ++) sobrecargar std :: swap, sin embargo, se le permite específicamente agregar especializaciones de plantillas para sus propios tipos al espacio de nombres std. P.ej
luego, los usos en los contenedores estándar (y en cualquier otro lugar) elegirán su especialización en lugar de la general.
También tenga en cuenta que proporcionar una implementación de clase base de intercambio no es lo suficientemente bueno para sus tipos derivados. Por ejemplo, si tienes
esto funcionará para las clases base, pero si intenta intercambiar dos objetos derivados, usará la versión genérica de std porque el intercambio con plantilla es una coincidencia exacta (y evita el problema de intercambiar solo las partes 'base' de sus objetos derivados ).
NOTA: He actualizado esto para eliminar los bits incorrectos de mi última respuesta. ¡Oh! (gracias puetzk y j_random_hacker por señalarlo)
fuente
Si bien es correcto que generalmente no se deben agregar cosas al espacio de nombres std ::, se permite específicamente agregar especializaciones de plantilla para tipos definidos por el usuario. Sobrecargar las funciones no lo es. Esta es una diferencia sutil :-)
Una especialización de std :: swap se vería así:
Sin la plantilla <> bit, sería una sobrecarga, que no está definida, en lugar de una especialización, que está permitida. El enfoque sugerido de @ Wilka para cambiar el espacio de nombres predeterminado puede funcionar con el código de usuario (debido a que la búsqueda de Koenig prefiere la versión sin espacio de nombres) pero no está garantizado, y de hecho no se supone que lo haga (la implementación de STL debería usar el -std :: swap calificado).
Hay un hilo en comp.lang.c ++. Moderado con una larga discusión del tema. Sin embargo, la mayor parte tiene que ver con la especialización parcial (que actualmente no hay una buena manera de hacerlo).
fuente
vectorversión y se utilizará .::swapsobrecarga que agregó es más especializada que lastd::swapsobrecargavector, por lo que captura la llamada y ninguna especialización de esta última es relevante. No estoy seguro de cómo es un problema práctico (¡pero tampoco estoy afirmando que sea una buena idea!).