user5380624
user5380624

Reputation: 61

Why isn't in.good() the same as !in.fail()?

I've read that the following is an anti-pattern:

while(in.good()) {
    // ...
}

But this is preferred:

while(operation) {
}

However, the stream has a conversion operator to bool that returns !fail(). Isn't !fail() the same as good()? If not, why aren't these two functions symmetrical? I'd expect it to be similar to true == !false.

Upvotes: 6

Views: 413

Answers (3)

Dmitry Rubanovich
Dmitry Rubanovich

Reputation: 2627

in.good() is equal to: [ios documentation]

! ( in.eof() || in.fail() || in.bad() )

Upvotes: 0

rici
rici

Reputation: 241671

in.good() is true if the previous read operation succeeded without EOF being encountered. (It is possible for EOF to be encountered even if a read operation succeeds.) So if in.good() is false, either the previous read operation failed -- in which case, the next one will also fail -- or EOF was encountered -- in which case the next read operation will also fail. So if in.good() is false, the next read operation will definitely fail. If we want to know whether it is worth trying to do a read operation, we should test in.good().

But the fact that in.good() is true does not guarantee anything. Suppose that in ends with whitespace, which is normally the case since text files are required to end with a newline. Formatted read operations stop at the first whitespace character, and unget it back into the input stream, so if the previous read operation read the last data item, it still did not leave the stream at EOF, and in will still be good. [See note 1]. So if your loop looks like this:

while (in.good()) {
  in >> x;
  /* Do something with x assuming that it is valid */
}

then you won't notice when the last in >> x failed, and you'll end up using an undefined value of x in the last loop. (Probably the last value will be processed twice.)

The same thing would happen if your loop was while (!in.bad()), or while (!in.eof()).

What you really want is to check whether in >> x succeeded. Nothing else. And the classic way to do that is:

while (in >> x) {
  /* If we get here, x has a legitimate value */
  ...
}

in >> x just returns in, so that's exactly equivalent to

while (in >> x, in) {
  /* ... */
}

In other words, bool(in) is false precisely when the previous operation failed, and true precisely when it succeeded. That's not the same as in.good() because we don't (yet) want to test whether EOF was encountered.

Notes:

  1. As stated in the first paragraph, it is possible for a read to succeed and also set EOF. One way that can happen is that the file does not end with a newline character. That's pretty rare, but you shouldn't rule it out. And the fact that it can happen is precisely why in.bad() doesn't examine the eof flag: in.bad() being false definitely means that the previous read operation failed.

Upvotes: 2

R Sahu
R Sahu

Reputation: 206567

There's an excellent table at http://en.cppreference.com/w/cpp/io/basic_ios/good that explains state of an std::istream and the values various functions return.

The only time while ( stream.good() ) and while(stream) will be different is when the contents of stream have been successfully read and EOF has been reached. At that time, stream.good() returns true while (bool)stream evaluates to false.

Upvotes: 3

Related Questions