Concepts / Output Stream Iterators

Output Stream Iterators

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

  • Programming

Treating 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.

What do you think happens?

Suppose the input is the three numbers 4 9 2, and a loop reads them through an istream_iterator<int> and prints each value it dereferences. What gets printed?

  • Only the first number, 4

  • All three numbers, 4 then 9 then 2

  • Nothing, because the loop never touches cin directly

Reveal answer
Answer:

All three numbers, 4 then 9 then 2

The iterator presents the stream as a sequence. Each dereference yields the value the iterator currently refers to, and each increment moves on to the next value the stream can supply, so the loop walks through all three values in order.

Creating an istream_iterator

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

Code walkthrough
#include <iostream>
#include <iterator>

int main() {
    std::istream_iterator<int> ii(std::cin);
    std::istream_iterator<int> eos;
    while (ii != eos) {
        std::cout << *ii << std::endl;
        ++ii;
    }
}
  1. 1
    Line 4 creates ii, attached to the standard input stream and declared to produce int values.
  2. 2
    Line 5 creates eos with no stream argument. This default iterator marks the end of the input.
  3. 3
    Line 6 compares ii against eos, so the loop runs only while there is still a value to read.
  4. 4
    Line 7 dereferences ii to obtain the current value and prints it.
  5. 5
    Line 8 advances ii, moving the iterator on to the next value in the stream.
  • std::istream_iterator<int> names the value type: each read should produce an int
  • (std::cin) names the stream the iterator is attached to
  • eos is a default-constructed istream_iterator<int>, used as the end-of-input marker
  • *ii dereferences the iterator to get the value it currently refers to
  • ++ii advances the iterator to the next value the stream can supply
Output
4
9
2

The program never asks cin for a value directly. It works through the single object ii, which stands in for the whole input stream. From the loop's point of view, the input is a sequence it can walk through, even though the data is arriving from outside the program.

Choosing the Value Type

The value type is not decided by the stream. Reading with istream_iterator<string> asks for string values, while istream_iterator<int> asks for integer values from the same stream. Because the choice of type is yours, it is worth deciding it deliberately from the kind of data the input is supposed to contain.

Think of the value type as a promise about each item. Every read tries to produce one value of that type from the stream. If the input contains items the declared type cannot hold, the read fails, and the iterator's behavior at that point is what determines the rest of the output.

Why Iterators Come in Pairs

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 is the iterator that begins the reading, and the second is an iterator that marks the end of the input. In the program above, ii plays the first role and eos plays the second.

ii (begin)starts reading4first value9second value2third valueeos (end)no value, marks the endbegins at++++++ reaches end
How do the begin and end iterators together bound the sequence of values? The begin iterator ii starts at the first value, each increment moves to the next, and the end marker eos is the position that has no value to give.

The pair works together: the loop starts with ii and keeps going while ii is not equal to eos. Without the second member there is no agreed position at which the loop should stop, so the pair is what turns an unbounded stream into a sequence with a defined end.

Tracing a Read Loop Step by Step

Tracing means following the iterator through each pass of the loop and recording what it refers to and what the loop prints. Work through the program with the input 4 9 2 and watch the values appear one per iteration.

read 4printed 4read 9printed 9read 2printed 2ii equals eosloop ends, nothing printed++ then compare++ then compare++ then compare
On each iteration, which token is read from the stream, and at which step does the loop stop because ii now equals eos?
Worked example

Tracing the loop with input 4 9 2

Trace the loop in the program above when the input is 4 9 2, and state exactly what the program prints.

  1. 1
    Before the loop

    ii is attached to cin and refers to the first value, 4. eos is the end marker.

  2. 2
    Iteration 1

    ii is not equal to eos, so the body runs. *ii gives 4, which is printed. ++ii moves ii to 9.

  3. 3
    Iteration 2

    ii refers to 9, which is not the end marker. 9 is printed, and ++ii moves ii to 2.

  4. 4
    Iteration 3

    ii refers to 2. 2 is printed, and ++ii moves past the last value.

  5. 5
    Check after iteration 3

    There is nothing left to read, so ii now compares equal to eos. The loop condition fails and the loop ends.

Result

The program prints 4, 9, and 2, one per line, and then stops.

What Happens at End of Input

The loop stops because of what happens inside the iterator when there is no value left to extract. When the underlying extraction fails, the iterator moves into a state where it compares equal to the default end iterator. The loop's comparison against eos then succeeds in signalling that reading has finished.

holds a valuedereference gives itextraction failsno value left, or a baditemequals eosloop condition fails++ asks for next valueiterator takes end state
What state does the iterator move into when extraction fails or the stream reaches end of file, and why does it then compare equal to the end iterator?

The same mechanism explains a subtler behavior. If the input contains an item that cannot be read as the declared type, the extraction fails at that item even though more text remains. Reading stops there, and the loop ends early. Nothing in the loop complains about it, which is why this kind of error often looks like missing output rather than an error message.

Mistakes Beginners Make

  • Common mistake

    Creating the iterator without naming the stream

    Why it fails

    The iterator must know which stream it reads from. Without the stream, it is not attached to the input the program means to read.

    Do this instead

    Pass the intended stream in the parentheses, for example std::istream_iterator<int> ii(std::cin).

  • Common mistake

    Using only one iterator and no end marker

    What it looks like
    while (ii != ...) with no second iterator to compare against
    Why it fails

    Reading a sequence needs a defined end. A single iterator gives no agreed position at which the loop should stop.

    Do this instead

    Create the end marker as a default istream_iterator and compare the loop against it.

  • Common mistake

    Choosing a value type that does not match the data

    What it looks like

    istream_iterator<int> reading the input 3 abc

    Why it fails

    The read of abc cannot produce an int, so extraction fails there and the loop stops early, after printing only 3.

    Do this instead

    Decide the value type from the kind of data the input contains, and check that every item fits it.

  • Common mistake

    Expecting the iterator to write data back into the stream

Practice Tracing a Read Loop

MEDIUM

A program reads with istream_iterator<int> and prints each value, using the same loop as above. The input is 5 8 oops 7. Predict exactly what the program prints, and identify the step at which the output stops matching what you would expect from reading every number.

Hints
  • Follow the iterator through each iteration and note which token is read on each one.
  • Ask whether oops can be produced as an int, and what the iterator does when it cannot.

Key Takeaways

  • An istream_iterator treats an input stream as a read-only container of values that can be stepped through with dereference and increment.
  • Creating one requires naming the stream to read from and the value type each read should produce.
  • Input iterators are used in pairs: a begin iterator starts the reading, and a default istream_iterator serves as the end marker the loop compares against.
  • When extraction fails or input ends, the iterator becomes equal to the end marker, so the loop stops; an item that does not fit the value type can stop reading early.
  • When output is wrong, check the attached stream, the value type, and the end-marker comparison, in that order.

Related concepts

More concepts starting with O