27 Beginner The Hundred
The Last Line, Twice
three readings go in, four come out
#include <iostream>
#include <sstream>
int main() {
std::istringstream readings("10\n20\n30\n"); // a tiny data file: three readings
int value = 0, total = 0;
while (!readings.eof()) {
readings >> value;
total += value;
std::cout << "read " << value << '\n';
}
std::cout << "total = " << total << '\n';
}
Run it. Three numbers, one loop — what does it print?
Answer
The last reading is counted twice, and the total comes out half again too big.
Why¶
eof() is not a look-ahead — it reports that some previous read already ran off the
end, and nothing more. Extracting 30 stops at the newline behind it and leaves that
newline in the stream, so eofbit is still clear and the loop takes a fourth turn. That
fourth >> skips the trailing whitespace, runs out of input, and gives up before it
starts parsing: failbit goes up, but value is never touched and still holds 30 — C++11
made a failed parse store 0, yet here the parse never begins. Delete the trailing
newline and the demo prints three lines, which is how this bug hides until someone edits
the data file. Worse, failbit is not eofbit: feed the same loop "10 20 xx" and it never
terminates, since a stream stuck in fail state never reaches end of file either.
-Wall -Wextra say nothing — the loop is perfectly legal, just wrong.
The fix¶
Loop on the read itself: a stream converts to false exactly when its last read failed,
so test and read can never disagree — likewise while (std::getline(in, line)) for lines.
while (readings >> value) { // prints 10, 20, 30 — total = 60
total += value;
std::cout << "read " << value << '\n';
}
Takeaway: test whether the read succeeded, never whether the stream already failed.
Try it: g++ -std=c++17 main.cpp -o demo && ./demo