https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88034
Bug ID: 88034 Summary: std::ws doesn't set failbit when the stream is already at EOF Product: gcc Version: 8.2.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: libstdc++ Assignee: unassigned at gcc dot gnu.org Reporter: vz-gcc at zeitlins dot org Target Milestone: --- Consider the following test program: % cat -n stream_ws.cpp 1 #include <iostream> 2 #include <sstream> 3 4 int main() 5 { 6 std::istringstream is("123"); 7 8 std::cout << "eof/fail/bad bit values: " 9 << is.eofbit << ", " 10 << is.failbit << ", " 11 << is.badbit << "\n"; 12 13 std::cout << "Initial state: " << is.rdstate() << "\n"; 14 15 int n = -1; 16 is >> n; 17 std::cout << "After reading \"" << n << "\": " << is.rdstate() << "\n"; 18 19 is >> std::ws; 20 std::cout << "After ws: " << is.rdstate() << "\n"; 21 } With g++ 8.2.0 (Debian 8.2.0-9) the output is: eof/fail/bad bit values: 2, 4, 1 Initial state: 0 After reading "123": 2 After ws: 2 I.e. calling ws() for the stream which is already at EOF does _not_ set the failbit at line 19. When the same program is compiled with MSVS 15.9, the output is: eof/fail/bad bit values: 1, 2, 4 Initial state: 0 After reading "123": 1 After ws: 3 i.e. it does turn failbit on. I had originally thought that MSVS behaviour was wrong, but Microsoft engineer Billy Robert O'Neal III has convincingly explained that it is correct in this reply to https://developercommunity.visualstudio.com/content/problem/382896/regression-stdws-incorrectly-sets-failbit-when-the.html In case this URL is not accessible without Microsoft account, let me quote his reply here: --- start quoted reply --- The current std::ws behavior is correct, and the old behavior was incorrect. If you look at N4762 (the current C++ working paper) [istream.manip]/2 it says that ws: 1. Behaves as an unformatted input function. 2. If it stops because it encounters the end, sets eofbit but not failbit. In particular, the unformatted input function processing happens before anything about this specific unformatted input operation is applied. The unformatted input rules are described in [istream.unformatted]/1, starting with: > Each unformatted input function begins execution by constructing an object of > class sentry with the default argument noskipws (second) argument true The behavior of constructing a sentry is described in [istream::sentry]/2, which begins with: > Effects: If is.good() is false, calls is.setstate(failbit) This ordering is more directly supported by ws' specific wording in [istream.manip]/2 which says (emphasis mine): > *After constructing a sentry object* extracts characters as long as the next > available character c is whitespace or until there are no more characters in > the sequence. --- end quoted reply --- So it looks like ws() is indeed supposed to set failbit here and it's a bug in libstdc++ that it doesn't. Note that if the stream were not already at EOF (e.g. if there were a trailing space after "123" in the line 6 of the example above), then failbit should not, and is not, correctly, being set by both libstdc++ and MSVS.