C++ Standard Library · Lesson 7

Iterators & Ranges

The "cursor" that lets one algorithm work on every container — what begin()/end() really mean, why some algorithms reject some containers, and how C++20 ranges make it all readable.

🎯 Understand the glue that connects containers to algorithms
The one idea

An iterator is a generalized pointer — a cursor you advance with ++ and read with *. A range is just a pair [begin, end). Because every container speaks "iterator," one std::sort fits all — and C++20 ranges let you skip the begin()/end() boilerplate entirely.

01 Iterators are generalized pointers

From Lesson 1: containers and algorithms meet through iterators. An iterator behaves like a pointer — *it reads the element, ++it moves to the next. begin() points at the first element; end() points one past the last — a sentinel you never dereference.

10 20 30 40 — begin() end() one past last half-open range: [ begin, end ) loop while (it != end())
The half-open [begin, end) convention means an empty range is just begin == end, and size is end - begin. It's everywhere in the library.
// the explicit form — this is what range-for desugars into
for (auto it = v.begin(); it != v.end(); ++it)
    std::cout << *it << " ";       // *it reads the element

// the sugar you actually write
for (const auto& x : v)
    std::cout << x << " ";

Source: cppreference — Iterator library.

02 Iterator categories — why some algorithms are picky

Not all iterators can do all things. A linked list's cursor can step forward and back but can't jump to "element 500"; a vector's can. The library grades iterators into categories, and each algorithm requires a minimum:

CategoryCan doContainers
Input / Outputsingle-pass read or write, ++streams
Forwardmulti-pass, ++forward_list, unordered_*
Bidirectional+ -- (step back)list, map, set
Random-access+ it + n, it[n], jump anywheredeque
Contiguous C++17+ elements adjacent in memoryvector, array, string
⚠ Why std::sort(myList...) won't compile std::sort needs random-access iterators (it jumps around). std::list only offers bidirectional ones — so you use the member myList.sort() instead. When an algorithm "doesn't work" on a container, an iterator-category mismatch is usually why.

Source: cppreference — Iterator categories.

03 Everyday iterator idioms

find returns an iterator

Most "where is it?" functions return an iterator, or end() for "not found": auto it = find(v.begin(), v.end(), 7); if (it != v.end()) …

Insert iterators

std::back_inserter(v) turns "write here" into "push_back onto v" — lets algorithms grow a destination. (Used with transform/copy, Lesson 8.)

cbegin, rbegin

cbegin() gives a read-only (const) iterator; rbegin() walks backwards. Same range idea, different cursor.

💡 Iterator invalidation (recurring theme) Operations that restructure a container can invalidate its iterators — a vector reallocation (Lesson 2), or erasing from one. Don't keep using an iterator after the container changed shape under it.

04 Ranges — the C++20 upgrade

Passing v.begin(), v.end() to every algorithm is noisy and lets you accidentally mix iterators from two containers. Ranges (C++20) let an algorithm take the whole container, and add views — lazy, composable transformations you pipe with |.

#include <algorithm>
#include <ranges>

std::vector<int> v = {5, 3, 1, 4, 2};

std::ranges::sort(v);            // no begin()/end() — pass the container itself

// views: lazily keep evens, then square them — composed left to right
auto result = v
    | std::views::filter([](int n){ return n % 2 == 0; })
    | std::views::transform([](int n){ return n * n; });

for (int x : result)
    std::cout << x << " ";       // 4 16   (2², 4²)
💡 Views are lazy and cheap A view doesn't copy or compute anything until you iterate it. The filter | transform pipeline above does no work until the for loop pulls values through — and it never builds an intermediate vector. Read pipelines top-to-bottom like a Unix pipe.

Source: cppreference — Ranges library (C++20).

05 Old style vs ranges

Iterators (classic)Ranges (C++20)
Sortstd::sort(v.begin(), v.end())std::ranges::sort(v)
Findstd::find(v.begin(), v.end(), x)std::ranges::find(v, x)
Transform+filternested loops or temp vectorsv | views::filter(..) | views::transform(..)
Mix-up riskcan pass a.begin(), b.end() by mistakeone argument — can't mismatch
🎯 What to take away You must read classic iterator code (it's everywhere), and you'll write ranges when you have C++20. Both rest on the same [begin, end) idea. The next lesson is the algorithms themselves — the verbs that act on these ranges.

06 Check yourself

What does a container's end() iterator point to?
Why does std::sort work on a vector but not a std::list?
A C++20 view like v | views::filter(...) is:
Classic std::find(v.begin(), v.end(), x) returns what when x is absent?

07 Flashcards

Q: What is an iterator?
A: A generalized pointer — a cursor over a container. *it reads the element, ++it advances, it == end() means done.
Q: What does the half-open range [begin, end) mean?
A: From the first element up to but not including end() (one past the last). Empty range = begin == end; size = end - begin.
Q: Why do iterator categories matter?
A: Each algorithm needs a minimum capability. sort needs random-access; a list gives only bidirectional, so std::sort rejects it (use list::sort).
Q: What's the ranges version of std::sort(v.begin(), v.end())?
A: std::ranges::sort(v) — pass the container directly; no begin/end, no chance of mismatched iterators.
Q: What is a view, and what makes it efficient?
A: A lazy, composable adaptor over a range (e.g. views::filter, views::transform) piped with |. It computes on demand and builds no intermediate container.
Q: What invalidates an iterator?
A: Structural changes to the container — e.g. a vector reallocation, or erasing elements. Don't reuse an iterator after the container changed shape.
👩‍🏫 I'm your teacher — ask me anything

Try: "rewrite this raw iterator loop as a ranges pipeline", or "why is end() one-past-the-end and not the last element?" Paste a loop and I'll show you the algorithm that replaces it.