Skip to content
C++
Library
since C++11
Intermediate

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++11

std::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:

FacilityHeaderSincePurpose
std::swap(a, b)<utility> (C++11; <algorithm> before)C++98Exchange two objects
std::iter_swap(it1, it2)<algorithm>C++98Exchange the objects two iterators refer to
std::exchange(obj, new)<utility>C++14Assign 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:

cpp
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

cpp
#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++20

Examples

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:

cpp
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:

cpp
#include <concepts>
static_assert(std::swappable<int>);       // C++20
// std::ranges::swap(a, b);               // C++20, performs ADL lookup correctly

Providing 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.

cpp
#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 net

Here 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

cpp
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

cpp
#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

cpp
#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 like std::vector use std::move_if_noexcept when 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 (or std::ranges::swap since C++20) in templates.
  • Provide both a member swap and a non-member swap for value-like types. The member can reach private state; the friend enables ADL.
  • Prefer std::exchange in move constructors for pointers, handles, and counters. It states intent and avoids forgetting to null the source.
  • Prefer member c.swap(d) or std::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-provided swap for T, 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::swap is not noexcept, 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::array and end() 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 exchange is atomic or thread-safe. It is a plain read-then-write.
  • std::exchange return type. It returns T by 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::swap copies. A user-provided swap mattered much more for performance in C++98/03 code.

See Also

  • std::move and std::forward (C++11), for the semantics underlying the generic swap
  • std::atomic<T>::exchange, for thread-safe replacement
  • std::ranges::swap and std::swappable (C++20)
  • std::unique_ptr::swap and std::unique_ptr::reset
  • Rule of Five and copy-and-swap, for move semantics design