Welcome back. In our last lesson, we established a clear framework for exception safety by defining the basic, strong, and no-throw guarantees. We saw that the strong guarantee, with its "commit-or-rollback" promise, is the most user-friendly, as it leaves the program state unchanged if an operation fails.
Today, we transition from theory to practice. You will learn to implement the strong exception safety guarantee for your classes using one of the most important and idiomatic techniques in C++: the copy-and-swap idiom. Mastering this pattern is essential for writing robust, resource-managing classes, and it is a topic frequently explored in senior C++ interviews to gauge a candidate's understanding of exception safety and modern language features.
The Challenge of a Correct Assignment Operator
For any class that manages a resource (like dynamic memory, a file handle, or a network socket), the copy assignment operator (operator=) is notoriously tricky to implement correctly. A naive implementation can suffer from several critical flaws.
Let's examine a canonical example. The highly-rated Stack Overflow answer, "What is the copy-and-swap idiom?", provides an excellent in-depth explanation. We will use it as our guide for this lesson.
What is the copy-and-swap idiom?
This article provides a definitive guide to the copy-and-swap idiom. We'll start by analyzing a flawed implementation to understand the problems this idiom solves.
Please begin by reading the section titled "An in-depth explanation." Focus on the dumb_array class example and its first, "failed" implementation of the assignment operator. Read from the initial dumb_array class definition through the analysis of the "failed solution". Pay close attention to the three problems identified: The self-assignment check. The lack of a strong exception guarantee (and how the object's state can be corrupted). The duplication of code from the copy constructor.
As the article demonstrates, the naive operator= has serious liabilities. If new int[mSize] throws std::bad_alloc, the object *this is left in an inconsistent state: its mArray has been deleted, but mSize has already been updated. The class invariant is broken, and the object is corrupted. While a slightly better version can provide the strong guarantee, it does so at the cost of significant code duplication, which is a maintenance nightmare.
The Solution: Copy, then Swap
The copy-and-swap idiom elegantly solves all these problems by changing the order of operations. The core idea is simple but powerful:
- Copy: Create a temporary, independent copy of the source object's data. All resource allocation and other potentially-throwing operations happen here. If this step fails, the original object (
*this) is completely unaffected. - Swap: Once the copy is successfully created, swap the data of the original object with the data of the temporary copy. This "commit" step should be non-throwing.
- Release: As the temporary object goes out of scope, its destructor is automatically called, releasing the old data that was swapped into it.
This sequence perfectly realizes the "commit-or-rollback" semantics. The "commit" is the fast, non-throwing swap. The "rollback" happens automatically if the copy construction fails.
The Building Blocks: A noexcept Swap Function
To implement the idiom, our class needs a crucial helper: a custom swap function that is guaranteed not to throw. We can't use the generic std::swap, as it's typically implemented using copy construction and copy assignment, which would lead to an infinite loop.
Our custom swap should simply exchange the underlying handles or pointers to the resources, not the resources themselves. Because this operation doesn't involve allocation or anything else that can fail, we should mark it noexcept.
The Copy-and-Swap Idiom – MC++ BLOG
The blog "Modernes C++" by Rainer Grimm gives a concise explanation of how to provide a proper swap function.
Read the section "The swap function". This section covers the C++ Core Guidelines recommendations for swap and shows the common pattern of providing both a member and a non-member swap.
The Idiom in Action
With a noexcept swap function in place, we can now write a perfect assignment operator. The most elegant implementation involves a subtle but profound change to the operator's signature: we take the parameter by value.
The following video provides an excellent step-by-step walkthrough, starting from the naive implementation and evolving it into the final, idiomatic form.
This video by Bradley Needham clearly demonstrates the evolution of an assignment operator towards the copy-and-swap idiom.
Watch the entire video (full duration). As you watch, note how each refinement addresses a specific problem: The initial attempt shows the exception-safety problem. The second attempt achieves strong safety but introduces code duplication. The third step introduces an explicit swap call. The final, idiomatic version passes the parameter by value, letting the compiler do the copy.
Let's recap the final implementation from the video and the Stack Overflow article:
#include <algorithm> // For std::swap
class MyClass {
int* _p_data;
// ... other members
public:
// ... constructor, copy constructor, destructor ...
friend void swap(MyClass& first, MyClass& second) noexcept {
using std::swap; // Enable ADL
swap(first._p_data, second._p_data);
// ... swap other members
}
MyClass& operator=(MyClass other) noexcept { // Pass by value!
swap(*this, other);
return *this;
}
};
This is remarkably concise and robust. Here’s why it works so well:
- Strong Guarantee: The copy is made before the function body is entered, during parameter passing. If the copy constructor throws,
operator=is never called, and*thisremains untouched. The swap itself isnoexcept, so the commit phase cannot fail. - No Code Duplication: We reuse the logic already present in the copy constructor to create the
otherobject. The destructor is reused to clean up the old state whenothergoes out of scope. - Self-Assignment Handled: If you write
x = x;, a copy ofxis made, the contents are swapped, and then the copy is destroyed. It works correctly without an explicitif (this != &other)check.
A Free Upgrade with C++11
This pass-by-value approach becomes even more powerful with the introduction of move semantics in C++11. If you also provide a move constructor (completing the Rule of Five), this single assignment operator handles both copy- and move-assignment optimally.
- If called with an lvalue (e.g.,
a = b;),otheris copy-constructed. - If called with an rvalue (e.g.,
a = std::move(b);ora = MyClass();),otheris move-constructed.
The compiler automatically chooses the most efficient way to create the other object. You get move-assignment semantics for free, without writing a separate operator=(MyClass&&).
A Common Pitfall: Ambiguous Swap
When implementing the swap call inside your assignment operator, a common mistake can lead to a compiler error.

This ambiguity arises because the compiler can find multiple swap functions that could potentially match the arguments. The standard way to resolve this and ensure the most specific swap is chosen is to use a using-declaration to bring std::swap into scope, and then make an unqualified call to swap.
// Inside a function or method...
using std::swap; // makes std::swap visible
swap(obj1, obj2); // unqualified call
This pattern allows Argument-Dependent Lookup (ADL) to find a specialized swap in the namespace of obj1 and obj2 if one exists. If not, it falls back to the generic std::swap. This is the canonical way to call swap in generic code and within your own classes.
Conclusion
You have now learned to implement the strong exception safety guarantee using the copy-and-swap idiom, a fundamental technique for any C++ developer building robust, resource-managing classes.
Here are the key takeaways:
- The Idiom: Copy-and-swap provides strong exception safety by first creating a temporary copy of the new state, and then committing the change with a non-throwing
swap. noexcept swap: The idiom relies on a customnoexcept swapfunction that efficiently exchanges the internal state of two objects.- Pass-by-Value: The most elegant implementation of the assignment operator takes its parameter by value. This leverages the copy/move constructor to handle resource acquisition and provides the strong guarantee automatically.
- C++11 Efficiency: A single pass-by-value assignment operator handles both copy and move assignment efficiently when a move constructor is also provided.
By mastering exception safety, you've learned to manage errors that are reported explicitly. However, C++ has a more insidious class of bugs that give no warnings. In our next lesson, we will explore these by examining the common causes of undefined behavior in C++.