C++
iter_swap
function specialization
programming
template specialization

Can iter_swap be specialised?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

std::iter_swap swaps the values pointed to by two iterators. While you cannot legally specialize std::iter_swap itself (specializing standard library function templates in namespace std is restricted), you can achieve the same optimization effect by customizing swap for your type or, in C++20, by providing a custom iter_swap via ADL for your iterator type through std::ranges::iter_swap.

What iter_swap Does

cpp
1#include <algorithm>
2#include <vector>
3
4int main() {
5    std::vector<int> v = {1, 2, 3, 4, 5};
6
7    // Swap elements at positions 0 and 4
8    std::iter_swap(v.begin(), v.begin() + 4);
9    // v = {5, 2, 3, 4, 1}
10}

The default implementation is equivalent to:

cpp
1template<class ForwardIt1, class ForwardIt2>
2void iter_swap(ForwardIt1 a, ForwardIt2 b) {
3    using std::swap;
4    swap(*a, *b);
5}

It dereferences both iterators and calls swap on the underlying values.

Can You Specialize std::iter_swap?

Short answer: No — per the C++ standard, you are only allowed to specialize templates in namespace std for user-defined types under specific conditions, and function templates like std::iter_swap are not among the allowed specializations. Adding overloads to namespace std is undefined behavior.

cpp
1// ILLEGAL — undefined behavior
2namespace std {
3    template<>
4    void iter_swap(MyIter a, MyIter b) { /* custom swap */ }
5}

What You Can Do Instead

Option 1: Specialize swap for Your Type

Since std::iter_swap calls swap(*a, *b) using ADL, you can provide a custom swap for your type:

cpp
1class Matrix {
2    double* data;
3    size_t rows, cols;
4public:
5    // Efficient swap — just swap pointers, not data
6    friend void swap(Matrix& a, Matrix& b) noexcept {
7        using std::swap;
8        swap(a.data, b.data);
9        swap(a.rows, b.rows);
10        swap(a.cols, b.cols);
11    }
12};
13
14// Now std::iter_swap on Matrix iterators uses your efficient swap
15std::vector<Matrix> matrices = { /* ... */ };
16std::iter_swap(matrices.begin(), matrices.begin() + 1);  // Calls your swap

Option 2: Custom Iterator with iter_swap (C++20)

In C++20, std::ranges::iter_swap uses ADL to find a custom iter_swap for your iterator type:

cpp
1#include <ranges>
2
3class ProxyIterator {
4    // ...
5public:
6    using value_type = int;
7
8    // Custom iter_swap found via ADL
9    friend void iter_swap(ProxyIterator a, ProxyIterator b) {
10        // Custom swap logic for proxy references
11        auto temp = *a;
12        *a = *b;
13        *b = temp;
14    }
15};
16
17// std::ranges::iter_swap will find the friend function via ADL
18ProxyIterator it1, it2;
19std::ranges::iter_swap(it1, it2);  // Calls your custom iter_swap

This is particularly useful for proxy iterators (like std::vector<bool>::iterator) where dereferencing returns a proxy reference, not a real reference.

Option 3: ADL-Based iter_swap in Your Namespace

cpp
1namespace mylib {
2    class BigObject {
3        std::unique_ptr<Data> data_;
4    public:
5        friend void swap(BigObject& a, BigObject& b) noexcept {
6            a.data_.swap(b.data_);
7        }
8    };
9
10    // Custom iter_swap for your iterators
11    template<typename Iter>
12    void iter_swap(Iter a, Iter b) {
13        // Only matches iterators to mylib types via ADL
14        swap(*a, *b);
15    }
16}

Why Customization Matters

The default iter_swap calls swap(*a, *b). For most types, this is fine. But for:

  • Large objects: A custom swap that moves pointers instead of copying data is orders of magnitude faster
  • Proxy iterators: vector<bool>::iterator returns a proxy, not bool&. The default swap fails because you cannot take a reference to a proxy. A custom iter_swap handles this correctly.
  • Non-copyable types: Move-only types need swap to use moves, not copies
cpp
1// Without custom swap: 3 copies of a large object
2// With custom swap: 3 pointer swaps
3struct LargeBuffer {
4    std::array<char, 1000000> data;
5
6    friend void swap(LargeBuffer& a, LargeBuffer& b) noexcept {
7        // Still copies, but you could use unique_ptr internally
8        std::swap(a.data, b.data);
9    }
10};

C++20 ranges::iter_swap Customization Points

std::ranges::iter_swap follows a priority order:

  1. Custom iter_swap found via ADL (your overload)
  2. std::ranges::swap(*a, *b) if both are lvalue references
  3. std::ranges::swap with moves as a fallback
cpp
// C++20 ranges algorithms use ranges::iter_swap internally
std::vector<int> v = {3, 1, 4, 1, 5};
std::ranges::sort(v);  // Internally calls ranges::iter_swap

Common Pitfalls

  • Never specialize in namespace std: Adding specializations or overloads of std::iter_swap in namespace std is undefined behavior. Use ADL (friend functions in your type's namespace) instead.
  • Forgetting using std::swap: Inside your custom swap implementation, always write using std::swap; before calling swap on members. This enables ADL for the member types while falling back to std::swap.
  • Proxy reference issues: If your iterator returns a proxy (not a true reference), std::swap(*a, *b) may fail to compile. Use C++20 ranges::iter_swap or provide a custom iter_swap via ADL.
  • noexcept: Mark your swap as noexcept if possible. Many STL algorithms check noexcept-ness to choose between move and copy operations.

Summary

  • You cannot legally specialize std::iter_swap in namespace std
  • Instead, provide a custom swap for your type — std::iter_swap will find it via ADL
  • In C++20, provide a friend iter_swap for your iterator type — std::ranges::iter_swap will find it via ADL
  • Custom swap is essential for large objects (pointer swap vs data copy) and proxy iterators
  • Always use using std::swap; before calling swap to enable ADL lookup

Course illustration
Course illustration

All Rights Reserved.