Iterators and Containers
An istream_iterator lets you treat an input stream as a read-only container of values.
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?
Reveal 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.
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.
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.
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.
- 1Check the stream
The iterator is attached to cin, which holds 4 7 x 9. The stream is correct.
- 2Check the value type
The type is int. The value x cannot be represented as an int, so it cannot be produced as a value.
- 3Check the end comparison
On reading x, the extraction fails, and ii becomes equal to eos. The loop condition ii != eos becomes false.
- 4Locate 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.
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.
Mistakes Beginners Make
Common mistake Choosing a value type that cannot represent the input
What it looks likeReading 4 7 x 9 with istream_iterator<int> and expecting all four values to print.
Why it failsThe 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 insteadDecide 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 failsThe iterator reads from whatever stream it was given, so the values come from that stream and nothing else.
Do this insteadConfirm 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 failsThe 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 insteadCreate 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
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_iteratorlets 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_iteratormarks the end. - When a read loop produces wrong output, check the attached stream, the value type, and the end-marker comparison, in that order.