0

I've looked at the standard and didn't see an obvious answer.

suppose i've done this:

std::istream_iterator<char> is(file);
while(is != std::istream_iterator<char>()) {
    ++is;
}

now is is at the end of the stream and is equal to std::istream_iterator<char>(). What happens if I increment it one more time? Is it still equal to std::istream_iterator<char>()? or os the result undefined?

The standard explicitly states that *is is undefined behavior if is is at the end of the stream. But I haven't seen anything regarding iterating beyond the end of the stream...

EDIT:

I ask because I stumbled across some code which does something like this:

// skip 2 input chars
++is;
++is;
if(is != std::istream_iterator<char>()) {
    // continue using is and do some work...but what if the first
    // increment made it EOS? is this check valid?
}
Evan Teran
  • 87,561
  • 32
  • 179
  • 238
  • What's the intent of continuing to iterate once you've hit the end of your data? – fbrereto Aug 20 '09 at 17:58
  • this is more of a curiosity than anything else. I stumbled upon some code which didn't always check EOS (but did check sometimes). The code appears to work fine...but I feel that is more of a coincidence and not how it is supposed to work... – Evan Teran Aug 20 '09 at 18:02

2 Answers2

4

Table 72 in C++03 about input iterator requirements says that the pre-condition of ++r is that r is de-referenceable. The same pre-conditions holds for r++.

Now, 24.5.1/1 says about istream_iterator

The result of operator* on an end of stream is not defined.

In conclusion, the effects of operator++ on an end-of-stream iterator are undefined.

Table 72 in C++03 about input iterator requirements says that the pre-condition of ++r is that r is de-referenceable. The same pre-conditions holds for r++.

Now, 24.5.1/1 says about istream_iterator

The result of operator* on an end of stream is not defined.

In conclusion, the effects of operator++ on an end-of-stream iterator are undefined.


Note that I think this conclusion makes behavior undefined only when you write or use an algorithm taking input iterators which exhibits that behavior, and then pass an istream iterator. From only using the istream iterator itself explicitly, without treating it as an input iterator and relying on its invariants, then i think the conclusion above isn't quite right (we may have a class that doesn't require that r is dereferenceable, for example).

But looking at how istream iterator is described, an invocation of operator++ after reaching the end of stream value results in undefined behavior either. operator== for it is defined as being equivalent to

x.in_stream == y.in_stream

Where in_stream is a pointer to the stream iterated over - and exposed into the Standard text for defining behavior and semantics "exposition only". Now, the only implementation i can think of that makes this work, is using an end-of-stream iterator that stores as stream pointer a null pointer. But operator++ is defined as doing something having the effect of the following

*in_stream >>value

Now, if you enter the end-of-stream state, and we would set in_stream to a null pointer, then surely the effect of that would be undefined behavior.

So even if you use the istream iterator alone, there doesn't seem to be any guarantee that you may increment past the end-of-stream value.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • 1
    Although your conclusion should probably be about `operator++`, rather than `operator*` :) – Daniel Earwicker Aug 20 '09 at 22:41
  • I looked at both GCC's and MSVC's implementation. With GCC you'll be "fine": ++ does nothing (checks first if you've reached the end) and iterator continues to compare equal to EOS. In MSVC you'd first go through iterator debugging and safe STL, but should you somehow make it to *in_stream >> value, it checks that the pointer isn't NULL first. Not that this is something to rely on, it just explains why the code might work quite OK with a particular implementation. (Or may-be it checks first that there is at least one character in the file?) – UncleBens Aug 21 '09 at 06:16
  • I suspect that the iterator debugging would give an assertion failure if you try to increment after you are at the end. How does it handle that? – Johannes Schaub - litb Aug 21 '09 at 14:05
3

If it isn't defined, it is undefined :-) In the standard (latest draft of C++0X) words, emphasis is mine:

undefined behavior

behavior, such as might arise upon use of an erroneous program construct or erroneous data, for which this International Standard imposes no requirements. Undefined behavior may also be expected when this International Standard omits the description of any explicit definition of behavior.

Community
  • 1
  • 1
AProgrammer
  • 51,233
  • 8
  • 91
  • 143
  • 1
    Yep, and it's common knowledge that undefined behavior usually manifests in _very_ nasty nasal demons coming down upon you. Ugh. `:)` – sbi Aug 20 '09 at 18:21
  • I would be happier if there were something which more specifically spelling it out, but so far this is looking to be correct. – Evan Teran Aug 20 '09 at 18:24
  • @sbi, the standard doesn't write about nasal demons :-) the section I cited continues just with "[Note: permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message)...]" Even non causal behavior is possible. – AProgrammer Aug 20 '09 at 18:29
  • However, to know whether it's not defined, you have to search through the whole Standard :) – Johannes Schaub - litb Aug 20 '09 at 22:29
  • @AProgrammer: I did write _"common knowledge"_, not "standardized". `:^>` – sbi Aug 21 '09 at 10:02