Concepts / Understanding Iterator Types

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.

  • Programming

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.

Entry Aposition 0Entry Bposition 1Entry Cposition 2Entry Dposition 3
Which element does an iterator refer to by position, and how do ++ and * map that position onto the sequence?

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.

Key point

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.

Best practice

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.

iterator at Entry A* gives Entry Aiterator at Entry B* gives Entry B++
After applying ++ once to an iterator that starts at Entry A, which element does * now return?
Worked example

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?

  1. 1
    Start

    The iterator refers to position 0, which holds Entry A.

  2. 2
    First ++

    ++ yields an iterator that refers to the next element, position 1, which holds Entry B.

  3. 3
    Second ++

    Applying ++ again yields an iterator that refers to position 2, which holds Entry C.

  4. 4
    Apply *

    * yields the element to which the iterator refers, so the element at position 2 is returned.

Result

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.

UnderstandingIterator Typesthis sectioniterator_traitsidea 1type predicatesidea 2introducesintroducesrelated to
Which idea is this section connecting, and what is the single relationship it states between them?

Mistakes Beginners Make with Iterators

  • Common mistake

    Believing an iterator must inherit from a special base class

    Why it fails

    The 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 instead

    Check 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 instead

    Treat ++ as moving the position and * as reading the element at that position.

  • Common mistake

    Memorizing a separate name for every iterator type

    Why it fails

    Containers make their iterator types available under the conventional names iterator and const_iterator.

    Do this instead

    Reach the type through the container, as in list<Entry>::iterator.

  • Common mistake

    Guessing what iterator_traits does from its name

    Why it fails

    This section names iterator_traits and type predicates without defining them.

    Do this instead

    Record 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?

  • Entry A

  • Entry B

  • Entry C

  • Entry D

Reveal answer
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.

EASY

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. 1
    ++ yields an iterator that refers to the next element, and * yields the element to which the iterator refers.
  2. 2
    Any object that obeys these simple rules counts as an iterator, so behavior defines the category.
  3. 3
    Containers make iterator and const_iterator available under conventional names, as in list<Entry>::iterator.
  4. 4
    Programmers usually do not need the internal definition of an iterator type.
  5. 5
    This section names iterator_traits and type predicates and states one link: iterator_traits is 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_iterator under 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_traits is related to type predicates; both are named here without definitions and should be studied further.

Related concepts

More concepts starting with U