Swap and Exchange
std::swap, std::exchange, and std::iter_swap: semantics, noexcept rules, the ADL swap idiom, and how to implement move operations and copy-and-swap correctly.
std::swap / std::exchangesince C++11std::swap (in <utility>) exchanges the values of two objects using move operations, while std::exchange (since C++14) replaces an object's value with a new one and returns the previous value.
Overview
Swapping is one of the most heavily used primitives in the library. Sorting, partitioning, std::remove, container assignment, and move constructors all depend on it. Three related facilities matter in practice:
| Facility | Header | Since | Purpose |
|---|---|---|---|
std::swap(a, b) | <utility> (C++11; <algorithm> before) | C++98 | Exchange two objects |
std::iter_swap(it1, it2) | <algorithm> | C++98 | Exchange the objects two iterators refer to |
std::exchange(obj, new) | <utility> | C++14 | Assign new value, return old |
Before C++11, std::swap was implemented with copy construction and copy assignment. Since C++11 the generic version uses moves:
template<class T>
void swap(T& a, T& b) { // C++11 semantic equivalent
T tmp(std::move(a));
a = std::move(b);
b = std::move(tmp);
}The generic version is constrained (C++11 in practice, formally required since C++17) to participate only if T is move constructible and move assignable. It is noexcept exactly when both operations are noexcept. Since C++20 it is also constexpr.
Overloads for T[N] arrays swap element-wise and are noexcept if the element swap is. Arrays of different extents do not compile.
Standard containers, std::pair, std::tuple (both since C++11 member and non-member swap), std::optional and std::variant (since C++17), and std::unique_ptr provide specialized overloads. For node-based and std::vector containers, swapping is O(1): internal pointers are exchanged and no elements are moved. std::array is the exception: it swaps in O(N), since the elements live inline.
Syntax
#include <utility>
#include <algorithm>
template<class T> void swap(T& a, T& b) noexcept(/*see below*/); // C++11
template<class T, size_t N> void swap(T (&a)[N], T (&b)[N]) noexcept(/*...*/); // C++11
template<class T, class U = T>
T exchange(T& obj, U&& new_value); // C++14; constexpr since C++20; noexcept since C++23
template<class It1, class It2>
void iter_swap(It1 a, It2 b); // C++98; constexpr since C++20
// C++20 ranges customization point object
// std::ranges::swap(a, b); // <concepts>, C++20Examples
The ADL swap idiom
std::swap is a customization point. Generic code must not call std::swap qualified, since that blocks user-provided overloads. Use the two-step idiom:
template<class T>
void generic_exchange(T& a, T& b) noexcept(noexcept(swap(a, b))) {
using std::swap; // fall back to std::swap
swap(a, b); // ADL finds a better overload if one exists
}Since C++20, std::ranges::swap encapsulates this idiom and is the preferred spelling in constrained code:
#include <concepts>
static_assert(std::swappable<int>); // C++20
// std::ranges::swap(a, b); // C++20, performs ADL lookup correctlyProviding a swap for your own type
Provide a noexcept friend (or namespace-scope) function in the type's own namespace. Do not add overloads to namespace std; that is undefined behavior, and specializing is only allowed for program-defined types and is discouraged.
#include <utility>
#include <string>
#include <vector>
namespace net {
class Buffer {
public:
Buffer() = default;
explicit Buffer(std::size_t n) : data_(new std::byte[n]), size_(n) {}
Buffer(Buffer&& o) noexcept
: data_(std::exchange(o.data_, nullptr)) // C++14
, size_(std::exchange(o.size_, 0)) {}
Buffer& operator=(Buffer&& o) noexcept {
Buffer tmp(std::move(o));
swap(tmp);
return *this;
}
Buffer(const Buffer&) = delete;
~Buffer() { delete[] data_; }
void swap(Buffer& o) noexcept {
using std::swap;
swap(data_, o.data_);
swap(size_, o.size_);
}
friend void swap(Buffer& a, Buffer& b) noexcept { a.swap(b); }
private:
std::byte* data_ = nullptr;
std::size_t size_ = 0;
};
} // namespace netHere std::exchange expresses "take the value and leave the source in a valid empty state" in one expression, which is the canonical use in move constructors.
Copy-and-swap assignment
class Config {
public:
Config(const Config&) = default;
Config(Config&&) noexcept = default;
Config& operator=(Config other) noexcept { // by value: copy or move happens at call site
swap(other);
return *this;
}
void swap(Config& o) noexcept {
using std::swap;
swap(name_, o.name_);
swap(values_, o.values_); // O(1) for vector
}
private:
std::string name_;
std::vector<int> values_;
};This gives the strong exception guarantee for the copy step and a trivially correct self-assignment. The cost is that it can be slower than hand-written assignment that reuses existing capacity.
std::exchange beyond moves
#include <utility>
// Reset-and-read a flag (C++14)
bool consume(bool& dirty) { return std::exchange(dirty, false); }
// Loop with a running "previous" value
int prev = 0;
for (int x : {3, 5, 9}) {
int delta = x - std::exchange(prev, x); // C++14
}Note: std::exchange is not atomic. For concurrent use, see std::atomic<T>::exchange.
iter_swap in algorithms
#include <algorithm>
#include <vector>
// Stable-ish compaction: move kept elements to the front
template<class It, class Pred>
It compact(It first, It last, Pred keep) {
It out = first;
for (It in = first; in != last; ++in)
if (keep(*in)) std::iter_swap(out++, in);
return out;
}std::iter_swap works on proxy iterators (such as std::vector<bool>::iterator) where std::swap(*a, *b) would fail, because *a yields a prvalue proxy. Since C++11, std::swap on vector<bool>::reference is not available generically, but iter_swap is permitted to be overloaded for this purpose.
Best Practices
- Mark swap
noexcept. Containers likestd::vectorusestd::move_if_noexceptwhen reallocating; a throwing move or swap forces copies. Swap is a building block for strong-guarantee code, so it should never throw. - Use the
using std::swap; swap(a, b);idiom (orstd::ranges::swapsince C++20) in templates. - Provide both a member
swapand a non-memberswapfor value-like types. The member can reach private state; the friend enables ADL. - Prefer
std::exchangein move constructors for pointers, handles, and counters. It states intent and avoids forgetting to null the source. - Prefer member
c.swap(d)orstd::swap(c, d)on containers over manual element copying. Both are O(1) for most containers. - Skip hand-rolled swaps on types that are already cheap and correct under the generic version. A custom swap is worthwhile only when it can beat three moves, for example by swapping a few pointers.
Common Pitfalls
- Qualified calls in templates.
std::swap(a, b)in a template ignores a user-providedswapforT, silently using three moves. - Self-swap.
std::swap(x, x)performs move-assign from a moved-from object in the generic version. A well-behaved type must tolerate self-move-assignment, though the resulting value is only guaranteed valid-but-unspecified for library types. - Throwing moves. If a type's move can throw,
std::swapis notnoexcept, and the strong guarantee can be lost mid-swap. - Swapping arrays of different sizes.
int a[10]; int b[11]; std::swap(a, b);does not compile because the types differ. - Iterator invalidation semantics. Swapping containers does not invalidate iterators or references (except for
std::arrayandend()iterators in several containers), but they now refer to elements in the other container. Allocator propagation (propagate_on_container_swap) affects behavior: with unequal non-propagating allocators, swap is undefined behavior. - Assuming
exchangeis atomic or thread-safe. It is a plain read-then-write. std::exchangereturn type. It returnsTby value, so exchanging large objects moves the old value out. That is fine for moves, but wasteful if the old value is discarded; use plain assignment then.- Pre-C++11 code. Before C++11,
std::swapcopies. A user-providedswapmattered much more for performance in C++98/03 code.
See Also
std::moveandstd::forward(C++11), for the semantics underlying the generic swapstd::atomic<T>::exchange, for thread-safe replacementstd::ranges::swapandstd::swappable(C++20)std::unique_ptr::swapandstd::unique_ptr::reset- Rule of Five and copy-and-swap, for move semantics design