Understanding Iterator Types
The defining feature of an iterator is behavior, not membership in one specific class. If an object's ++ moves it to the next element and its * yields the element it refers to, it is an iterator for the purposes of this section.
Why Iterators Look Alike
Suppose you have a collection of items and you want to visit each one in turn. Many different kinds of collections exist, and each may store its items in a different way. Yet when you look at the tools used to move through them, a surprising pattern appears. The tools look alike. The same operations keep showing up, and they mean the same things no matter which collection they came from. That shared behavior is what makes a family of objects called iterators.
The question this section answers is simple: what do all iterators have in common? The short answer is their semantics, meaning what their operations do, together with the naming of those operations. Once you understand those two things, you can work with iterators from many different containers without relearning the rules each time.
Moving and Reading: The Two Shared Operations
An iterator is tied to two operations that capture almost everything you do with one. Each one answers a different question: where the iterator goes next, and what it is currently pointing at. The sections below take the two operations one at a time, and then show them working together.
Tracing an Iterator's Position
The diagram below is a generated illustration, not taken from the source. It shows a sequence of four elements, each stored at a numbered position. An iterator can be thought of as a marker that refers to one of these positions. Applying * reads the element under the marker, and applying ++ moves the marker to the next position.
Read the diagram from left to right. Each box is one element, and its position number tells you where it sits. An iterator that refers to position 0 gives Entry A when you apply *. Applying ++ to that iterator produces an iterator that refers to position 1, so applying * then gives Entry B. The sequence itself does not change; only the iterator's reference moves.
What Counts as an Iterator
A natural first guess is that an iterator must belong to some special family of classes, with a required parent and a fixed structure. This section takes a different view. What makes an object an iterator is that it obeys a few simple rules, such as the ones for ++ and *. Any object that follows rules like these is an iterator.
The defining feature of an iterator is behavior, not membership in one specific class. If an object's ++ moves it to the next element and its * yields the element it refers to, it is an iterator for the purposes of this section.
This is why one set of habits works across many collections. A loop written around ++ and * does not need to know whether the thing being advanced is a simple reference into a sequence or a custom object. As long as the shared operations behave as described, the same reasoning applies.
Finding Iterator Types Through Their Container
Most of the time you do not need to know the exact type of a particular iterator. Each container knows which iterator types belong to it, and it makes them available under conventional names. Those names are iterator and const_iterator. You reach the type through the container, not by memorizing a separate name for each one.
The source gives one concrete example. The general iterator type for a list of Entry elements is written list<Entry>::iterator. The name combines the container type, list<Entry>, with the conventional member name iterator. Reading the name this way tells you which container the iterator belongs to, even when you do not know anything else about its definition.
Why Internal Definitions Stay Hidden
Programmers usually do not need the internal definition of an iterator type. You can rely on the shared semantics of ++ and *, and on the conventional naming, without knowing how each iterator type is built inside. The interface is the part you work with; the internal definition is the part that stays out of sight for ordinary use.
Write code against the shared behavior of ++ and * and the conventional names, rather than against the details of one particular iterator's internals. This keeps your reasoning the same across containers.
Following One Step from Start to Finish
The example below is generated to show how the two operations change state. The before and after panels use the same four-element sequence as before. Only the iterator's reference changes; the elements stay where they are.
Walking forward two steps
An iterator starts at position 0 of the four-element sequence Entry A, Entry B, Entry C, Entry D. Apply ++ twice, then apply *. What element is returned?
- 1Start
The iterator refers to position 0, which holds Entry A.
- 2First ++
++ yields an iterator that refers to the next element, position 1, which holds Entry B.
- 3Second ++
Applying ++ again yields an iterator that refers to position 2, which holds Entry C.
- 4Apply *
* yields the element to which the iterator refers, so the element at position 2 is returned.
Entry C
Iterator Traits and Type Predicates
This section introduces two named ideas that belong to the chapter on type functions in C++: iterator_traits and type predicates. Both are presented here as topics to understand, and the section connects them to each other. The one relationship the section states is that iterator_traits is related to type predicates.
Mistakes Beginners Make with Iterators
Common mistake Believing an iterator must inherit from a special base class
Why it failsThe source says what makes an object an iterator is that it obeys a few simple rules, not that it belongs to a required family of classes.
Do this insteadCheck whether the object's ++ and * behave as described.
Common mistake Thinking ++ changes the element stored in the sequence
Why it fails++ yields an iterator that refers to the next element. It moves the reference, and the element it refers to is unchanged by that move.
Do this insteadTreat ++ as moving the position and * as reading the element at that position.
Common mistake Memorizing a separate name for every iterator type
Why it failsContainers make their iterator types available under the conventional names iterator and
const_iterator.Do this insteadReach the type through the container, as in list<Entry>::iterator.
Common mistake Guessing what iterator_traits does from its name
Why it failsThis section names
iterator_traitsand type predicates without defining them.Do this insteadRecord the stated relationship and write down open questions for later study.
Checking Your Prediction
What do you think happens?
An iterator refers to Entry B in the four-element sequence Entry A, Entry B, Entry C, Entry D. You apply ++ and then *. What do you get?
Reveal answer
Entry C
++ yields an iterator that refers to the next element, so the iterator now refers to Entry C. Applying * yields the element it refers to, which is Entry C.
Write three specific questions you would need to answer to understand iterator_traits in more depth. Then write one question about how type predicates relate to it. Keep the stated link separate from your open questions.
Hints
- Start from what this section says the two ideas are related by, not from a guessed definition.
- Good questions ask what a specific term means, what it is used for, or how it is applied.
Key Points to Carry Forward
- 1++ yields an iterator that refers to the next element, and * yields the element to which the iterator refers.
- 2Any object that obeys these simple rules counts as an iterator, so behavior defines the category.
- 3Containers make iterator and
const_iteratoravailable under conventional names, as in list<Entry>::iterator. - 4Programmers usually do not need the internal definition of an iterator type.
- 5This section names
iterator_traitsand type predicates and states one link:iterator_traitsis related to type predicates. Their definitions are left for further study.
Key Takeaways
- ++ yields an iterator that refers to the next element, and * yields the element to which the iterator refers.
- Any object that obeys these simple rules counts as an iterator, so the defining feature is behavior rather than class membership.
- Containers expose iterator and
const_iteratorunder conventional names, as in list<Entry>::iterator. - Programmers usually do not need the internal definition of an iterator type and can rely on shared semantics and naming.
iterator_traitsis related to type predicates; both are named here without definitions and should be studied further.