Iterator Concept
An iterator is defined by its semantics and the naming of its operations, not by one fixed type. Any object that obeys the rules is an iterator.
Why You Rarely Need the Type Name
Suppose you are writing a loop that walks through a collection of records. You need a way to point at one record, move on to the next, and read the record you are pointing at. The language provides a tool for exactly this job, called an iterator. A natural first question is which specific iterator type you should write down.
The answer this article builds toward is that, for most day-to-day work, you do not need to know that exact type. What you need to know is how iterators behave, and where to find the type name when you do need it. Before reading on, try the question below.
What do you think happens?
A list holds Ada, Grace, and Alan, in that order. An iterator starts out referring to Ada. You apply the increment operator once, then the dereference operator. What do you get back?
Reveal answer
Grace
Increment moves the iterator forward so it refers to the next element, which is Grace. Dereference then yields the element the iterator currently refers to, which is Grace.
An Iterator Is a Role, Not a Type
An iterator is defined by its semantics and the naming of its operations, not by one fixed type. Any object that obeys the rules is an iterator.
Think of an iterator less as a particular kind of thing and more as a role that many different objects can play. What makes an object an iterator is that it responds to the same operations in the same way. The operations are named consistently across all iterators, and their meanings are shared. Whatever the underlying object looks like, if it honors these shared meanings, it counts as an iterator.
This is why the concept is easier to learn through behavior than through a list of type names. Two objects with completely different internal structure can both be iterators, as long as the same operations behave the same way on each. A caller who knows the operations can work with either one without caring what the underlying object is.
Increment Moves, Dereference Reads
The two core operations pull in different directions. The increment operator changes which element the iterator points to, moving it forward to the next one. Applying increment to an iterator yields an iterator that refers to the next element.
The dereference operator does not move anything. It reads through the iterator and gives you the element currently being referred to. Applying dereference yields the element to which the iterator refers.
Keep the two roles separate. One step moves the pointer, and the other step looks at what the pointer now refers to. Once you read a loop with that split in mind, each line becomes easier to predict.
Tracing an Iterator Through Three Names
Work through a concrete case. The list holds three names in order: Ada, Grace, and Alan. The iterator starts out referring to Ada. Each arrow in the diagram below is one application of the increment operator, and each reading is what dereference would return at that moment.
Reading the iterator at each step
The iterator starts at Ada. Apply increment, then dereference, then increment, then dereference. Which names does dereference return?
- 1Start
The iterator refers to Ada. No move has happened yet, so dereference would return Ada.
- 2First increment
Increment moves the iterator so it refers to the next element, Grace. The list itself is unchanged.
- 3First dereference
Dereference reads the element currently referred to, so it yields Grace. Dereference does not move the iterator.
- 4Second increment
Increment again advances the reference by one element, from Grace to Alan.
- 5Second dereference
Dereference now yields Alan, the element the iterator currently refers to.
The dereference calls return Grace, then Alan.
How a Container Advertises Its Iterator Types
If iterators are defined by shared behavior, you might expect every program to name a separate iterator type for every container it uses. The source describes a convenience instead. Each container knows the iterator types that belong to it, and it makes those types available under two conventional names: iterator and const_iterator.
The name iterator therefore comes from the container, not from a global rule. From outside the container, you reach it by qualifying the container's name, as in list<Entry>::iterator. The same qualifying pattern is how you would reach the other conventional name.
| Name | Provided by | How a caller reaches it | What the source states |
|---|---|---|---|
| iterator | The container | Qualified name such as list<Entry>::iterator | Identifies the general iterator type of that container |
| const_iterator | The container | Qualified name using the same container, such as list<Entry>::const_iterator | Made available under this conventional name alongside iterator; the source does not detail its behavior here |
The two conventional iterator names a container makes available
Reading list<Entry>::iterator
A qualified name such as list<Entry>::iterator identifies the general iterator type of that container. Read it from left to right. The part list<Entry> names the container, with Entry as the element type it holds. The double colon then reaches into that container to get the nested name iterator, which is the iterator type the container provides.
A loop over a list of Entry values declares its position with list<Entry>::iterator. Reading that declaration, you can say it is a position into a list of Entry elements, and you do not need to know how the iterator type is defined internally to use it. The source notes that you rarely need that internal detail.
Mistakes Learners Make with Iterator Names
Common mistake Believing that every container needs a separate iterator type name memorized in advance
What it looks likeTrying to recall a unique name for the iterator of each collection in a program
Why it failsEach container knows its own iterator types and makes them available under the conventional names iterator and
const_iteratorDo this insteadLook up the type through the container, for example list<Entry>::iterator
Common mistake Expecting the dereference operator to move the iterator forward
What it looks likeAssuming that reading the element with dereference also advances to the next one
Why it failsDereference does not move anything. It only reads the element currently referred to
Do this insteadUse increment to move, and dereference to read
Common mistake Expecting the increment operator to return the element itself
What it looks likeTreating the result of applying increment as the name Grace
Why it failsApplying increment yields an iterator that refers to the next element, not the element's value
Do this insteadApply dereference to the moved iterator to get the element
Practice Tracing a Position
A list holds Ada, Grace, and Alan in that order. An iterator starts out referring to Ada. Apply the increment operator twice, then apply the dereference operator. Which element do you get, and where does the iterator refer afterward?
Hints
- Each increment moves the iterator forward by one element.
- Dereference does not change the position. It only reads what the iterator currently refers to.
- Count the increments from Ada: the first gives Grace and the second gives Alan.
Key Takeaways
- An iterator is defined by its semantics and the naming of its operations, not by one fixed type; any object that obeys the rules is an iterator.
- Increment moves the iterator to refer to the next element, while dereference reads and yields the element the iterator currently refers to without moving it.
- Each container knows its own iterator types and makes them available under the conventional names iterator and
const_iterator, so you look them up through the container. - A qualified name such as list<Entry>::iterator identifies the general iterator type of that container, and you rarely need to know how that type is defined.