Qué es el idiom Copy-and-Swap y por qué es imprescindible en 2025
Imagina que estás escribiendo una clase para trabajar con memoria dinámica. Todo va bien hasta que llegas a la sobrecarga del operador de asignación. Escribes operator=, verificas la autoasignación, liberas manualmente los recursos antiguos, asignas nuevos… Y en el 90% de los casos cometes un error que provocará una fuga de memoria o una doble eliminación. ¿Te suena?
En C++ existe una solución elegante: el idiom copy-and-swap. No es solo un truco moderno, sino una técnica fundamental que permite escribir código seguro, resistente a excepciones y conciso. En 2025, cuando C++23 ya se usa activamente y el estándar C++26 está en el horizonte, este idiom sigue siendo el estándar de oro para las clases que gestionan recursos.
En este artículo, analizaremos el idiom copy-and-swap de principio a fin: desde el problema que resuelve hasta ejemplos prácticos y las mejores prácticas de 2025. ¡Prepara tu compilador, que habrá mucho código!
El problema: por qué el operator= ingenuo es malo
Comencemos con un ejemplo clásico de una clase que gestiona un arreglo dinámico. Sin el idiom copy-and-swap, el código a menudo se ve así:
class DumbArray {public: DumbArray(size_t size) : size_(size), data_(new int[size]) {} ~DumbArray() { delete[] data_; } // Operador de asignación ingenuo DumbArray& operator=(const DumbArray& other) { if (this == &other) return *this; // verificación de autoasignación delete[] data_; // liberamos recursos antiguos size_ = other.size_; data_ = new int[size_]; // asignamos nuevos std::copy(other.data_, other.data_ + size_, data_); return *this; } private: size_t size_; int* data_;};A primera vista, todo parece lógico. Pero hay tres problemas:
- Falta de garantía básica de excepciones. Si
new int[size_]lanzastd::bad_alloc, el objeto queda en un estado incorrecto:data_apuntará a memoria eliminada. - Duplicación de lógica. La copia de datos ya está implementada en el constructor de copia. ¿Por qué escribimos lo mismo manualmente?
- Verificación de autoasignación. Ralentiza el código y es fácil de olvidar. Además, en el 95% de los casos no ocurre autoasignación, pero la verificación se ejecuta igualmente.
Y eso no es todo. Si agregas otros recursos a la clase (como descriptores de archivos o mutexes), el código no solo se vuelve voluminoso, sino peligroso. Aquí es donde entra en escena el idiom copy-and-swap.
La esencia del idiom: tres pasos hacia un operator= perfecto
El idiom copy-and-swap se basa en una idea simple: no escribas la lógica de copia en el operador de asignación; usa el constructor de copia y la función swap. El algoritmo es el siguiente:
- Copy: creamos una copia temporal del objeto usando el constructor de copia.
- Swap: intercambiamos el contenido del objeto actual con la copia temporal.
- Destroy: el objeto temporal se destruye, liberando los recursos antiguos.
En código, se ve así:
class SmartArray {public: SmartArray(size_t size = 0) : size_(size), data_(size ? new int[size] : nullptr) {} // Constructor de copia SmartArray(const SmartArray& other) : size_(other.size_), data_(other.size_ ? new int[other.size_] : nullptr) { std::copy(other.data_, other.data_ + size_, data_); } // Destructor ~SmartArray() { delete[] data_; } // Función swap amiga friend void swap(SmartArray& first, SmartArray& second) noexcept { using std::swap; swap(first.size_, second.siz