std::swap vs std::exchange vs swap operator
Categories:
Mastering Value Swapping in C++: std::swap vs. std::exchange vs. Custom Swap

Explore the nuances of value swapping in C++11/14 and beyond, comparing std::swap
, std::exchange
, and custom swap
operators to optimize performance and ensure correctness.
Swapping the values of two variables is a fundamental operation in programming. In C++, there are several ways to achieve this, each with its own use cases, advantages, and potential pitfalls. This article delves into the standard library functions std::swap
and std::exchange
(introduced in C++14), as well as the concept of providing a custom swap
operator for user-defined types. Understanding their differences is crucial for writing efficient, correct, and idiomatic C++ code.
std::swap: The General-Purpose Swapper
std::swap
is the most common and versatile way to exchange the values of two objects. It's defined in the <utility>
header (or <algorithm>
for older C++ versions) and typically implemented using move semantics for efficiency. For fundamental types, it performs a simple three-assignment swap using a temporary variable. For user-defined types, it will either use a specialized std::swap
overload or, by default, fall back to move-constructing a temporary and then move-assigning. This behavior makes it robust for a wide range of types.
#include <iostream>
#include <utility> // For std::swap
#include <vector>
int main() {
int a = 10;
int b = 20;
std::cout << "Before std::swap: a = " << a << ", b = " << b << std::endl;
std::swap(a, b);
std::cout << "After std::swap: a = " << a << ", b = " << b << std::endl;
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 = {4, 5, 6, 7};
std::cout << "\nBefore std::swap: v1 size = " << v1.size() << ", v2 size = " << v2.size() << std::endl;
std::swap(v1, v2);
std::cout << "After std::swap: v1 size = " << v1.size() << ", v2 size = " << v2.size() << std::endl;
return 0;
}
Basic usage of std::swap
with integral types and std::vector
.
swap
for your class, it's often best to provide a non-member, non-friend swap
function in the same namespace as your class. This allows Argument-Dependent Lookup (ADL) to find your specialized swap
when std::swap
is called unqualified.std::exchange: Swapping and Returning the Old Value
Introduced in C++14, std::exchange
(from <utility>
) serves a slightly different, more specific purpose than std::swap
. It assigns a new value to an object and returns the object's old value. This is particularly useful in scenarios where you need to retrieve the current state of an object before modifying it, often seen in move constructors, assignment operators, or state-management functions.
#include <iostream>
#include <utility> // For std::exchange
class MyResource {
public:
int value;
MyResource(int v = 0) : value(v) { std::cout << "MyResource(" << value << ") constructed\n"; }
MyResource(MyResource&& other) noexcept : value(std::exchange(other.value, 0)) {
std::cout << "MyResource move constructed from " << other.value << " (old value) to " << value << " (new value)\n";
}
~MyResource() { std::cout << "MyResource(" << value << ") destructed\n"; }
};
int main() {
int current_state = 100;
int new_state = 200;
// Assign new_state to current_state, and get the old value of current_state
int old_state = std::exchange(current_state, new_state);
std::cout << "\nstd::exchange example:\n";
std::cout << "Old state: " << old_state << std::endl; // 100
std::cout << "Current state: " << current_state << std::endl; // 200
std::cout << "\nMyResource move constructor example:\n";
MyResource r1(50);
MyResource r2 = std::move(r1); // Uses move constructor with std::exchange
std::cout << "r1.value after move: " << r1.value << std::endl; // 0
std::cout << "r2.value after move: " << r2.value << std::endl; // 50
return 0;
}
Demonstrating std::exchange
for value replacement and in a move constructor.
flowchart TD A[Initial State] --> B{Call std::exchange(obj, new_val)} B --> C[Store obj's current value in temp] C --> D[Assign new_val to obj] D --> E[Return temp (obj's old value)] E --> F[Final State]
Process flow of std::exchange
.
Custom Swap Operators: Optimizing for User-Defined Types
For complex user-defined types, especially those managing resources (like dynamically allocated memory or file handles), the default std::swap
(which relies on move construction and assignment) can be inefficient. A custom swap
operator can often perform a more efficient exchange by directly swapping the internal resources or pointers, avoiding unnecessary deep copies or allocations. This is a common idiom in C++ for implementing the copy-and-swap idiom for strong exception safety.
#include <iostream>
#include <utility> // For std::swap
#include <vector>
class MyVectorWrapper {
private:
std::vector<int> data;
public:
MyVectorWrapper(std::initializer_list<int> il) : data(il) {
std::cout << "MyVectorWrapper constructed with size " << data.size() << "\n";
}
// Custom non-member swap function (friendship is optional but common for access)
friend void swap(MyVectorWrapper& first, MyVectorWrapper& second) noexcept {
using std::swap; // Enable ADL
swap(first.data, second.data); // Swaps internal std::vector objects efficiently
std::cout << "Custom swap called!\n";
}
void print_size() const {
std::cout << "Size: " << data.size() << "\n";
}
};
int main() {
MyVectorWrapper mv1 = {1, 2, 3};
MyVectorWrapper mv2 = {4, 5, 6, 7, 8};
std::cout << "\nBefore swap:\n";
mv1.print_size(); // Size: 3
mv2.print_size(); // Size: 5
// Call std::swap, which will find our custom swap via ADL
using std::swap; // Bring std::swap into scope
swap(mv1, mv2);
std::cout << "\nAfter swap:\n";
mv1.print_size(); // Size: 5
mv2.print_size(); // Size: 3
return 0;
}
Implementing a custom swap
for a user-defined type to efficiently exchange internal resources.
swap
for your class, ensure it's noexcept
if possible. Swapping operations should ideally not throw exceptions, as this can complicate exception safety guarantees, especially in the copy-and-swap idiom.classDiagram class MyVectorWrapper { -std::vector<int> data +MyVectorWrapper(initializer_list<int>) +print_size() const } class std::vector<int> { // ... internal vector members } MyVectorWrapper "1" -- "1" std::vector<int> : contains note for MyVectorWrapper "Custom non-member swap function is defined in the same namespace to enable ADL."
Class diagram illustrating MyVectorWrapper
and its internal std::vector
.
In summary, std::swap
is your go-to for general-purpose swapping, leveraging move semantics or custom overloads. std::exchange
is perfect for atomically replacing a value and retrieving the old one. For user-defined types with complex resource management, a custom swap
operator can provide significant performance benefits and improve exception safety. Choosing the right tool for the job is key to writing effective C++.