c++ - ejemplo - swap c
¿Cómo implementa la biblioteca estándar std:: swap? (2)
¿Cómo se implementa la función de intercambio en el STL?
¿Qué implementación? Es una especificación, no una sola biblioteca concreta. Si quiere decir cómo lo hace la biblioteca estándar de mi compilador , díganos qué compilador es o lea el código usted mismo.
Es tan simple como esto:
Esa es esencialmente la versión ingenua anterior a C ++ 11.
Esta implementación no especializada fuerza una copia: para T = std::vector<SomethingExpensive>
en su ejemplo, el código se traduce como:
template<typename T> void swap(T& t1, T& t2) {
T tmp(t1); // duplicate t1, making an expensive copy of each element
t1=t2; // discard the original contents of t1,
// and replace them with an expensive duplicate of t2
t2=tmp; // discard the original contents of t2,
// and replace them with an expensive duplicate of tmp
} // implicitly destroy the expensive temporary copy of t1
entonces para intercambiar dos vectores esencialmente creamos tres . Hubo tres asignaciones dinámicas y muchos objetos costosos copiados, y cualquiera de esas operaciones podría arrojarse, posiblemente dejando los argumentos en un estado indeterminado.
Dado que esto era obviamente horrible, se proporcionaron sobrecargas para contenedores costosos, y se le animó a escribir sobrecargas para sus propios tipos costosos: ej. la especialización std::vector
tenía acceso a las partes internas del vector, y podía intercambiar dos vectores sin toda la copia:
template <typename T> void swap(vector<T> &v1, vector<T> &v2) { v1.swap(v2); }
template <typename T> void vector<T>::swap(vector<T>& other) {
swap(this->size_, other.size_); // cheap integer swap of allocated count
swap(this->used_, other.used_); // cheap integer swap of used count
swap(this->data__, other.data_); // cheap pointer swap of data ptr
}
Tenga en cuenta que esto no implica ninguna copia de nada costoso, no hay asignación dinámica (de), y se garantiza que no se lanzará.
Ahora, la razón de esta especialización es que vector :: swap tiene acceso a las partes internas del vector y puede moverlas de manera segura y eficiente sin necesidad de copiar.
¿Por qué tendría que hacer esto [especializando ... para su propia clase]?
Pre-C ++ 11, por la misma razón que std::vector
: hacer que el intercambio sea eficiente y seguro para excepciones.
Desde C ++ 11, realmente no lo hace, si proporciona la construcción y la asignación de movimientos, o el compilador puede generar valores predeterminados en su sano juicio.
El nuevo intercambio genérico:
template <typename T> void swap(T& t1, T& t2) {
T temp = std::move(t1);
t1 = std::move(t2);
t2 = std::move(temp);
}
puede usar mover construcción / asignación para obtener esencialmente el mismo comportamiento que la implementación de vector personalizado anterior, sin necesidad de escribir una implementación personalizada en absoluto.
¿Cómo se implementa la función de intercambio en el STL? Es tan simple como esto:
template<typename T> void swap(T& t1, T& t2) {
T tmp(t1);
t1=t2;
t2=tmp;
}
En otras publicaciones, hablan sobre la especialización de esta función para su propia clase. ¿Por qué tendría que hacer esto? ¿Por qué no puedo usar la función std::swap
?
¿Cómo se implementa std::swap
?
Sí, la implementación presentada en la pregunta es la clásica C ++ 03.
Una implementación más moderna (C ++ 11) de std::swap
ve así:
template<typename T> void swap(T& t1, T& t2) {
T temp = std::move(t1); // or T temp(std::move(t1));
t1 = std::move(t2);
t2 = std::move(temp);
}
Esta es una mejora con respecto a la implementación clásica de C ++ 03 en términos de administración de recursos porque evita copias innecesarias, etc. El C ++ 11 std::swap
requiere que el tipo T
sea MoveConstructible y MoveAssignable , lo que permite la implementación y las mejoras.
¿Por qué tendría que proporcionar una implementación personalizada?
Por lo general, se recomienda una implementación personalizada de swap
, para un tipo específico, cuando su implementación es más eficiente o específica que la versión estándar.
Un ejemplo clásico de esto es cuando su clase maneja una gran cantidad de recursos que sería costoso copiar y luego eliminar. En cambio, su implementación personalizada simplemente podría intercambiar los identificadores o punteros necesarios para efectuar el intercambio.
Con el advenimiento de std::move
y los tipos móviles (e implementado su tipo como tal), alrededor de C ++ 11 en adelante, muchos de los fundamentos originales están comenzando a desaparecer; pero, sin embargo, si un intercambio personalizado sería mejor que el estándar, impleméntelo.
El código genérico generalmente podrá usar su swap
personalizado si usa el mecanismo ADL apropiada.