Intermediate · The Standard Template Library (STL) · topic 24 of 42 · 25 min
Iterators: the glue between containers and algorithms
Explanation
Why iterators?
A vector stores elements in a row, a set in a tree, a list in linked nodes. If every algorithm had to know every container's insides, the library would need hundreds of versions of sort and find. Iterators solve this. An iterator is a small object that behaves like a pointer: *it gives the element, ++it moves to the next one. Each container provides its own iterators, and algorithms only talk to iterators.
#include <iostream>
#include <vector>
int main() {
std::vector<int> v = {10, 20, 30};
for (auto it = v.begin(); it != v.end(); ++it) {
std::cout << *it << ' '; // 10 20 30
}
auto it = v.begin();
*it = 99; // change through the iterator
std::cout << '\n' << v[0] << '\n'; // 99
}The half-open range [begin, end)
end() does not point to the last element; it points one past it. It is a "stop here" marker and must never be dereferenced. This convention makes empty ranges easy (begin == end) and loop conditions simple (it != end).
Kinds of iterators
| Category | Can do | Found in |
|---|---|---|
| Forward | ++it, *it, read a sequence once or many times | std::forward_list, unordered containers |
| Bidirectional | also --it | std::list, std::set, std::map |
| Random access | also it + n, it[n], it2 - it1, < | std::vector, std::deque, std::array, raw pointers |
Algorithms ask for the category they need. std::sort needs random access, so you cannot std::sort a std::list (use list.sort() instead). std::find only needs to move forward, so it works everywhere.
const iterators and reverse iterators
std::vector<int> v = {1, 2, 3};
for (auto it = v.cbegin(); it != v.cend(); ++it) { /* *it is read-only */ }
for (auto it = v.rbegin(); it != v.rend(); ++it) std::cout << *it; // 321Map iterators
std::map<std::string, int> stock = {{"dal", 25}, {"rice", 40}};
auto it = stock.find("rice");
if (it != stock.end()) {
std::cout << it->first << " = " << it->second << '\n'; // rice = 40
it->second += 10; // value can change; key cannot
}Erasing while iterating
std::map<std::string, int> stock = {{"dal", 0}, {"rice", 40}, {"oil", 0}};
for (auto it = stock.begin(); it != stock.end(); ) {
if (it->second == 0) it = stock.erase(it); // erase returns the next valid iterator
else ++it;
}
// stock now has only riceDistance and advance
- std::distance(first, last) counts the steps between two iterators.
- std::next(it, n) and std::prev(it, n) give moved copies without changing it.
- For a vector, it - v.begin() is the index of the element.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Iterators: the glue between containers and algorithms · 1/6
Iterators connect containers to algorithms. They behave like pointers that know how to move through a container.
Prefer a video by a teacher? Find free videos on “c++ stl iterators begin end tutorial erase while iterating” on YouTube ↗
In real life: Cleaning an inventory list
A warehouse system keeps stock per product code in a std::map. Every night it removes products with zero stock and prints the rest. Erasing during the loop must use the iterator that erase returns, or the program would crash on the next step.
#include <iostream>
#include <map>
#include <string>
int main() {
std::map<std::string, int> stock = {{"P100", 0}, {"P200", 12}, {"P300", 0}, {"P400", 7}};
for (auto it = stock.begin(); it != stock.end(); ) {
if (it->second == 0) it = stock.erase(it);
else ++it;
}
for (const auto& [code, qty] : stock) std::cout << code << ' ' << qty << '\n';
}
// P200 12
// P400 7Practice (0/4 done)
For std::vector<int> v = {4, 5, 6}, what is *(v.begin() + 2)?
Which statement about v.end() is true?
Which line has the bug? for (auto it = m.begin(); it != m.end(); ++it) { if (it->second == 0) m.erase(it); }
Using only iterators (no indexes), write a function that returns the position of the first negative number in a std::vector<int>, or -1 if there is none. Then print the vector in reverse using rbegin/rend.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- STL algorithms: sort, find, count, accumulate and binary searchThe Standard Template Library (STL) · same module
- std::map and std::unordered_mapThe Standard Template Library (STL) · same module
- Iterators and the iteration protocolJavaScript Programming (language in depth) · Map, Set, iterators, generators and regular expressions
- C++20 ranges, views and other C++20 essentialsModern C++: lambdas, C++17 and C++20 essentials
- Iterators and generatorsPython Programming · Advanced Python
- IteratorsRust Programming · Closures, iterators and smart pointers
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
Why can you not call std::sort on a std::list?
Answer: std::sort needs random-access iterators; list iterators are only bidirectional
Use the member function list.sort() instead.
What does an empty container's begin() equal?
Answer: end()
An empty range has begin == end.
What does it->first give for a std::map iterator?
Answer: The key
Map elements are pairs: first is the key, second is the value.
What do rbegin() and rend() let you do?
Answer: Iterate from the last element back to the first
Reverse iterators walk the container backwards.
What does std::remove(v.begin(), v.end(), 0) do on its own?Related
Answer: Moves non-zero elements to the front and returns the new logical end; the size is unchanged
You must call erase with the returned iterator to actually shrink the vector.
From std::vector in depth
Which algorithm keeps equal elements in their original order?Related
Answer: std::stable_sort
stable_sort is stable; sort is not guaranteed to be.
From STL algorithms: sort, find, count, accumulate and binary search
What does an iterator's next() return?Related
Answer: An object { value, done }
next() returns { value, done }.
What is std::span<const int> good for?Related
Answer: Passing any contiguous array (vector, std::array, C array) to a function without copying
span is a non-owning view of contiguous elements.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 1 more topic in “The Standard Template Library (STL)” before its exam: STL algorithms: sort, find, count, accumulate and binary search.
- Module 6 exam: The STL · 13 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Templates, smart pointers and move semantics. 42 topics left in the course before the final exam.