c++language-lawyeriostreamsstream

What value should `std::stringstream::fail()` return after reading then writing? (gcc vs clang)


Consider the following code snippet:

#include <iostream>
#include <sstream>

int main()
{
    std::stringstream ss;
    ss << "12345";
    unsigned short s;
    ss >> s;
    ss << "foo";

    std::cout << std::boolalpha
              << "\nss.eof()  = " << ss.eof()
              << "\nss.good() = " << ss.good()
              << "\nss.bad()  = " << ss.bad()
              << "\nss.fail() = " << ss.fail()
              << "\nss.str()  = " << ss.str();
}

clang++ trunk prints the following result:

ss.eof()  = true
ss.good() = false
ss.bad()  = false
ss.fail() = false
ss.str()  = 12345

on wandbox


g++ trunk prints the following result:

ss.eof()  = true
ss.good() = false
ss.bad()  = false
ss.fail() = true
ss.str()  = 12345

on wandbox


As you can see, the value of ss.fail() is different between the two compilers. What does the Standard say regarding the behavior of std::stringstream in this case? Is it implementation-defined to set failbit/badbit when writing to a stream that has already been consumed?


Solution

  • Gcc is correct. std::stringstream inherits from std::basic_ostream, and according to the behavior of operator<<(std::basic_ostream) (which is invoked from ss << "foo";),

    Effects: Behaves like a formatted inserter (as described in [ostream.formatted.reqmts]) of out.

    And from §30.7.5.2.1/1 Common requirements [ostream.formatted.reqmts]:

    (emphasis mine)

    Each formatted output function begins execution by constructing an object of class sentry. If this object returns true when converted to a value of type bool, the function endeavors to generate the requested output. If the generation fails, then the formatted output function does setstate(ios_­base​::​failbit), which might throw an exception.

    And §30.7.5.1.3 Class basic_­ostream​::​sentry [ostream::sentry]:

    If os.good() is nonzero, prepares for formatted or unformatted output. If os.tie() is not a null pointer, calls os.tie()->flush().

    If, after any preparation is completed, os.good() is true, ok_­ == true otherwise, ok_­ == false. During preparation, the constructor may call setstate(failbit) (which may throw ios_­base​::​​failure ([iostate.flags]))

    And §30.5.5.4 basic_­ios flags functions [iostate.flags]:

    iostate rdstate() const;
    Returns: The error state of the stream buffer.

    bool good() const;
    Returns: rdstate() == 0

    bool eof() const;
    Returns: true if eofbit is set in rdstate().

    eofbit has been set in this case, then std::basic_ios::good returns nonzero and causes writing failed (as the result showed), then failbit should be set.