std::swap()
es utilizado por muchos contenedores estándar (como std::list
y 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::sort
que 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::algorithm
llamada 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 exp
lugar 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
swap
en 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
A
es una plantilla de clase:Ahora no vuelve a funcionar. :-(
Entonces podría poner
swap
en el espacio de nombres std y hacer que funcione. Sin embargo, usted tiene que recordar para ponerswap
enA
espacio de nombres 's para el caso cuando se tiene una plantilla:A<T>
. Y dado que ambos casos funcionarán si ponesswap
enA
el 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::swap
es una excepción a la regla de "nunca poner usando instrucciones dentro de los archivos de encabezado"? De hecho, ¿por qué no ponerlousing std::swap
dentro<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::swap
alcance de la función dentro de sus encabezados. Sí,swap
es 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.swap
es 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
vector
versión y se utilizará .::swap
sobrecarga que agregó es más especializada que lastd::swap
sobrecargavector
, 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!).