Skip to content
C++

Iterators Quick Reference

C++ iterator categories, requirements, traits, adapters, sentinel iterators, and C++20 ranges concepts.

Iterator Categories

cpp
InputIterator       → can read forward, single-pass (istream)
OutputIterator      → can write forward, single-pass (ostream, back_inserter)
ForwardIterator     → read+write forward, multi-pass (forward_list)
BidirectionalIterator → + can go backward (list, map, set)
RandomAccessIterator  → + jump by n, compare (vector, deque, array)
ContiguousIterator    → + contiguous memory (C++17: vector, array, string)

C++20 Concept Hierarchy

cpp
#include <iterator>

// Concepts (C++20)
std::input_iterator<I>            // supports *it, ++it
std::output_iterator<I, T>        // supports *it = val, ++it
std::forward_iterator<I>          // input + default-constructible + multipass
std::bidirectional_iterator<I>    // forward + --it
std::random_access_iterator<I>    // bidirectional + it+n, it[n], it1-it2
std::contiguous_iterator<I>       // random access + contiguous memory

// Range concepts
std::ranges::range<R>             // has begin() + end()
std::ranges::sized_range<R>       // + size()
std::ranges::contiguous_range<R>  // contiguous memory
std::ranges::viewable_range<R>    // can be converted to view

Iterator Traits

cpp
#include <iterator>

// Access category and value type of any iterator
using I = std::vector<int>::iterator;
std::iterator_traits<I>::value_type;         // int
std::iterator_traits<I>::difference_type;    // ptrdiff_t
std::iterator_traits<I>::reference;          // int&
std::iterator_traits<I>::pointer;            // int*
std::iterator_traits<I>::iterator_category;  // std::random_access_iterator_tag

// C++20 shorthand (concept-based)
std::iter_value_t<I>       // int
std::iter_reference_t<I>   // int&
std::iter_difference_t<I>  // ptrdiff_t

// Check category
using Cat = std::iterator_traits<I>::iterator_category;
std::is_same_v<Cat, std::random_access_iterator_tag>  // true

// With concepts (C++20):
std::random_access_iterator<I>  // true

Iterator Operations

cpp
std::vector<int> v{1,2,3,4,5};
auto it = v.begin();

// Universal (all categories)
*it         // dereference: 1
++it        // advance: points to 2

// Bidirectional+
--it        // retreat: back to 1

// Random access only
it + 3      // advance by 3 (doesn't modify it)
it[2]       // same as *(it + 2)
it2 - it1   // distance (signed)
it1 < it2   // comparison

// std::advance (works for all categories)
std::advance(it, 3);   // advance by 3 (uses += for random, ++ for others)
std::advance(it, -1);  // only for bidirectional+

// std::distance (works for all categories)
auto d = std::distance(v.begin(), v.end());  // 5

// std::next / std::prev (non-mutating)
auto it2 = std::next(it, 2);   // it + 2 (new iterator)
auto it3 = std::prev(it, 1);   // it - 1 (new iterator)

Iterator Adapters

cpp
// reverse_iterator
std::vector<int> v{1,2,3,4,5};
for (auto it = v.rbegin(); it != v.rend(); ++it)
    std::print("{} ", *it);  // 5 4 3 2 1

// back_insert_iterator
std::vector<int> dst;
std::copy(v.begin(), v.end(), std::back_inserter(dst));  // appends

// front_insert_iterator (deque, list)
std::deque<int> dq;
std::copy(v.begin(), v.end(), std::front_inserter(dq));  // prepends

// insert_iterator (at a position)
std::list<int> lst{1,2,3};
auto pos = std::next(lst.begin());
std::copy(v.begin(), v.end(), std::inserter(lst, pos));

// istream_iterator
std::istream_iterator<int> in{std::cin}, eof;
std::vector<int> nums{in, eof};

// ostream_iterator
std::ostream_iterator<int> out{std::cout, ", "};
std::copy(v.begin(), v.end(), out);  // 1, 2, 3, 4, 5,

// move_iterator (move elements instead of copy)
std::vector<std::string> src{"a", "b", "c"};
std::vector<std::string> dst2(std::make_move_iterator(src.begin()),
                              std::make_move_iterator(src.end()));
// src elements are moved (left in valid but unspecified state)

Sentinel Iterators (C++20)

A sentinel is a type that marks the end — it doesn't need to be the same type as the iterator:

cpp
// Null-terminated string range using sentinel
struct NullSentinel {};

struct CStrIterator {
    const char* ptr;
    char operator*() const { return *ptr; }
    CStrIterator& operator++() { ++ptr; return *this; }
    bool operator==(NullSentinel) const { return *ptr == '\0'; }
};

// Ranges-compatible: begin() returns CStrIterator, end() returns NullSentinel
struct CStrRange {
    const char* str;
    CStrIterator begin() const { return {str}; }
    NullSentinel end()   const { return {}; }
};

for (char c : CStrRange{"hello"})
    std::print("{}", c);  // h e l l o

Writing a Custom Iterator

cpp
// Random access iterator for a strided view
template<typename T>
class StridedIterator {
    T*     ptr_;
    size_t stride_;
public:
    using value_type        = T;
    using reference         = T&;
    using pointer           = T*;
    using difference_type   = std::ptrdiff_t;
    using iterator_category = std::random_access_iterator_tag;

    StridedIterator(T* p, size_t s) : ptr_{p}, stride_{s} {}

    reference operator*() const { return *ptr_; }
    pointer   operator->() const { return ptr_; }
    reference operator[](difference_type n) { return ptr_[n * stride_]; }

    StridedIterator& operator++() { ptr_ += stride_; return *this; }
    StridedIterator  operator++(int) { auto tmp = *this; ++*this; return tmp; }
    StridedIterator& operator--() { ptr_ -= stride_; return *this; }
    StridedIterator  operator--(int) { auto tmp = *this; --*this; return tmp; }

    StridedIterator  operator+(difference_type n) const { return {ptr_ + n*stride_, stride_}; }
    StridedIterator  operator-(difference_type n) const { return {ptr_ - n*stride_, stride_}; }
    difference_type  operator-(const StridedIterator& o) const { return (ptr_ - o.ptr_) / stride_; }

    auto operator<=>(const StridedIterator&) const = default;
};
Edit on GitHub