What is the Copy-and-Swap Idiom and Why is it a Must-Have in 2025
Imagine you are writing a class for working with dynamic memory. Everything goes well until you get to overloading the assignment operator. You write operator=, check for self-assignment, manually free old resources, allocate new ones… And in 90% of cases, you introduce a bug that leads to a memory leak or double deletion. Sound familiar?
In C++, there is an elegant solution — the copy-and-swap idiom. This is not just a fancy trick, but a fundamental technique that allows you to write safe, exception-resistant, and concise code. In 2025, when C++23 is already actively used and the C++26 standard is on the horizon, this idiom remains the gold standard for resource-managing classes.
In this article, we will break down the copy-and-swap idiom from start to finish: starting with the problem it solves, ending with practical examples and best practices for 2025. Get your compiler ready — there will be a lot of code!
The Problem: Why a Naive operator= is Evil
Let's start with a classic example of a class that manages a dynamic array. Without the copy-and-swap idiom, the code often looks like this:
class DumbArray {public: DumbArray(size_t size) : size_(size), data_(new int[size]) {} ~DumbArray() { delete[] data_; } // Naive assignment operator DumbArray& operator=(const DumbArray& other) { if (this == &other) return *this; // self-assignment check delete[] data_; // free old resources size_ = other.size_; data_ = new int[size_]; // allocate new ones std::copy(other.data_, other.data_ + size_, data_); return *this; } private: size_t size_; int* data_;};At first glance, everything is logical. But there are three problems:
- Lack of basic exception guarantee. If
new int[size_]throwsstd::bad_alloc, the object remains in an invalid state:data_will point to deleted memory. - Duplication of logic. Copying data is already implemented in the copy constructor. Why are we writing the same thing manually?
- Self-assignment check. It slows down the code and is easily forgotten. Moreover, in 95% of cases, self-assignment does not occur, but the check is still performed.
And that's not all. If you add other resources to the class (e.g., file descriptors or mutexes), the code becomes not just cumbersome — it becomes dangerous. This is exactly where the copy-and-swap idiom comes into play.
The Essence of the Idiom: Three Steps to the Perfect operator=
The copy-and-swap idiom is based on a simple idea: don't write the copy logic in the assignment operator — use the copy constructor and the swap function. The algorithm looks like this:
- Copy: create a temporary copy of the object using the copy constructor.
- Swap: exchange the contents of the current object with the temporary copy.
- Destroy: the temporary object is destroyed, freeing the old resources.
In code, it looks like this:
class SmartArray {public: SmartArray(size_t size = 0) : size_(size), data_(size ? new int[size] : nullptr) {} // Copy constructor 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_; } // Friend swap function friend void swap(SmartArray& first, SmartArray& second) noexcept { using std::swap; swap(first.size_, second.siz