Concepts / Iterators and Containers

Iterators and Containers

An istream_iterator lets you treat an input stream as a read-only container of values.

  • Programming

Reading Input as a Sequence

Many programs need to read a sequence of values from the keyboard or from a file until there is nothing left to read. A common first attempt is to write a loop that calls an input operation again and again, managing each read by hand. An istream_iterator takes a different route: it lets an input stream be handled as though it were a container of values that can be stepped through one element at a time.

The summary idea is short: an istream_iterator lets you treat an input stream as a read-only container of values. The rest of this article shows what that means in practice, and how to trace a loop built around one so you can see exactly where its behavior departs from what you expected.

What do you think happens?

Suppose the input stream holds the text 4 7 x 9, and the loop below reads it as int values. Which values get printed before the loop stops?

  • 4, 7, 9

  • 4, 7 only

  • 4 only

  • 4, 7, x, 9

Reveal answer
Answer:

4, 7 only

The iterator reads 4 and 7 as ints. When it tries to read x as an int, the extraction fails, the iterator becomes equal to the end marker, and the loop stops. The 9 is never reached. Working out why requires following the iterator's state, which the next sections teach.

Creating an istream_iterator

Creating an istream_iterator requires supplying two things: the input stream it will read from, and the type of value you expect each read to produce. In the declaration, the angle brackets name the value type and the parentheses name the stream. Changing either one changes what the iterator does.

python
Notes on the code
  • istream_iterator<int> names the value type: each read produces an int
  • ii(cin) attaches the iterator to the standard input stream
  • eos is default-constructed and serves as the end-of-input marker
  • *ii gives the value the iterator currently refers to
  • ++ii advances the iterator to the next value the stream can supply

Notice that this loop never asks cin for a value directly. It works through a single object, ii, which stands in for the whole input stream. Dereferencing ii with * gives the value it currently refers to, and advancing it with ++ moves to the next value. From the loop's point of view the input is a sequence it can walk through, even though the data arrives from outside the program.

Why Two Iterators Mark a Range

A single istream_iterator tells you where reading starts, but reading a sequence also needs to know where it stops. Input iterators are therefore used in pairs that together represent a sequence. The first member of the pair begins the reading, and the second marks the end of the input. In the code above, ii plays the first role and eos plays the second.

4first valueiibegins reading7second valuexfails as inteosend marker9never reachedbegins atmarks end at
How the two iterators delimit the sequence: ii begins at the first value, and eos marks the position where reading ends. Here the failed read of x is where the range closes.

The pair works because the loop compares ii against eos on every pass. As long as the two are unequal, there is a value to read. When they become equal, the sequence is exhausted and the loop must stop. The end marker is not a value in the input; it is a separate iterator that exists only to be compared against.

Tracing the Read Loop

Trace the loop on the input 4 7 x 9 with value type int. Before the loop body runs, ii refers to 4. Each pass compares ii with eos, prints the current value, and advances ii. The table of states below follows the iterator through each step.

ii = 4not equal to eos, print 4ii = 7not equal to eos, print 7ii = endread of x failed, loopstops++ii++ii fails on x
On each iteration, the iterator refers to one value, the loop body prints it, and ++ii advances. The output departs from expectation at the failed read of x, after which the iterator equals eos.
Worked example

Finding where the output diverges

The input is 4 7 x 9. The programmer expected the loop to print 4, 7, 9. Locate the step where the actual output departs from that expectation.

  1. 1
    Check the stream

    The iterator is attached to cin, which holds 4 7 x 9. The stream is correct.

  2. 2
    Check the value type

    The type is int. The value x cannot be represented as an int, so it cannot be produced as a value.

  3. 3
    Check the end comparison

    On reading x, the extraction fails, and ii becomes equal to eos. The loop condition ii != eos becomes false.

  4. 4
    Locate the divergence

    Output is correct for 4 and 7. The departure happens when the iterator tries to produce x. The 9 is never read, because the iterator never advances past the failure.

Result

The output is 4 and 7, and it departs from the expected 4, 7, 9 at the read of x. The cause is the value type and the failed extraction, not the loop structure.

The End Marker as a Sentinel

A default-constructed istream_iterator serves as the end-of-input marker. It is not attached to any stream, so it does not read anything. Its job is to be compared against. The iterator ii moves between two conditions: it refers to a value that was successfully read, or it has reached the end, where it compares equal to eos.

Readingii attached to streamHolds valuedereference gives valueEqual to eosloop exitsextraction succeeds++ii reads nextextraction fails or end-of-file
A successful extraction leaves ii holding a value. A failed extraction, which includes reaching end-of-file, moves ii into the state equal to eos, and the loop stops.

Mistakes Beginners Make

  • Common mistake

    Choosing a value type that cannot represent the input

    What it looks like

    Reading 4 7 x 9 with istream_iterator<int> and expecting all four values to print.

    Why it fails

    The value type is decided by you, not by the stream. An int cannot hold x, so the extraction fails and reading stops early.

    Do this instead

    Decide the type from the data the input is supposed to contain, and check that it can represent every item.

  • Common mistake

    Attaching the iterator to the wrong stream

    Why it fails

    The iterator reads from whatever stream it was given, so the values come from that stream and nothing else.

    Do this instead

    Confirm the stream argument in the declaration is the stream you intended before debugging the loop.

  • Common mistake

    Comparing against a different or missing end marker

    Why it fails

    The loop only stops when ii equals the end marker it compares against. A marker that is not properly created for the loop cannot signal the end correctly.

    Do this instead

    Create a default-constructed end iterator of the same value type and compare ii against it.

  • Common mistake

    Treating an istream_iterator as writable, when it only gives read access to values

Practice Tracing a Read Loop

MEDIUM

The loop from the earlier example reads with istream_iterator<int> from cin. The input is 12 5 oops 8. Trace the iterator through each pass. Which values are printed, at which step does output stop matching what was expected, and which of the three checks would reveal the cause?

Hints
  • Follow ii one read at a time, starting at 12.
  • Ask at which read the int extraction fails.
  • Once ii equals eos, does the loop body run again?

Key Takeaways

  • An istream_iterator lets you treat an input stream as a read-only container of values that you step through with * and ++.
  • Creating one requires naming the input stream and the value type each read should produce; both are part of the declaration.
  • Input iterators are used in pairs: the first begins reading, and a default-constructed istream_iterator marks the end.
  • When a read loop produces wrong output, check the attached stream, the value type, and the end-marker comparison, in that order.

Related concepts

More concepts starting with I