56

From Stroustrup's TC++PL, 3rd Edition, Section 21.3.3:

If we try to read into a variable v and the operation fails, the value of v should be unchanged (it is unchanged if v is one of the types handled by istream or ostream member functions).

The following example appears to contradict the above quote. Based on the above quote, I was expecting the value of v to remain unchanged -- but it gets zeroed. What's the explanation for this apparent contradictory behaviour?

#include <iostream>
#include <sstream>

int main( )
{
    std::stringstream  ss;

    ss  << "The quick brown fox.";

    int  v = 123;

    std::cout << "Before: " << v << "\n";

    if( ss >> v )
    {
        std::cout << "Strange -- was successful at reading a word into an int!\n";
    }

    std::cout << "After: " << v << "\n";

    if( ss.rdstate() & std::stringstream::eofbit  ) std::cout << "state: eofbit\n";
    if( ss.rdstate() & std::stringstream::failbit ) std::cout << "state: failbit\n";
    if( ss.rdstate() & std::stringstream::badbit  ) std::cout << "state: badbit\n";

    return 1;
}

The output I get using x86_64-w64-mingw32-g++.exe (rubenvb-4.7.2-release) 4.7.2 is:

Before: 123
After: 0
state: failbit

Thanks.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
user1823664
  • 1,071
  • 9
  • 16
  • Based on Joachim Pileborg's explanation, it's good to mention the compiler flags in such questions; the flags I'm using: `-Wall -Wextra -Werror -static -std=c++11` – user1823664 Nov 14 '12 at 12:57

2 Answers2

63

From this reference:

If extraction fails (e.g. if a letter was entered where a digit is expected), value is left unmodified and failbit is set (until C++11)

If extraction fails, zero is written to value and failbit is set. If extraction results in the value too large or too small to fit in value, std::numeric_limits::max() or std::numeric_limits::min() is written and failbit flag is set. (since C++11)

It seems that your compiler is compiling in C++11 mode, which changes the behavior.


The input operator uses the locale facet std::num_get whose get function invokes do_get. For C++11 it's specified to use std::strtoll et. al. type of functions. Before C++11 it apparently used std::scanf style parsing (going by the reference, I don't have access to the C++03 specification) to extract the numbers. The change in behavior is due to this change in parsing the input.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • 7
    Interesting change, I never knew. I always treat reading a variable after a failed extraction as undefined behaviour... – Kerrek SB Nov 14 '12 at 12:48
  • Does anyone know the rationale behind this change? – Alex Nov 14 '12 at 12:49
  • @Alex: It sounds more in line with what `strtod` etc. do (which always return a definite value), and it gives you some sort of information on the way the extraction failed, I suppose. Being out of range is different from being simply not parsable, I guess. – Kerrek SB Nov 14 '12 at 12:51
  • @Alex: Could be to make implementation of the string conversion functions easier (http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#1169). Can't find precisely where this wording change was proposed, though. – Lightness Races in Orbit Nov 14 '12 at 12:51
  • @KerrekSB Reading the C++11 specification it actually _uses_ those functions (§22.4.2.1.2, section 3, "stage" 3). – Some programmer dude Nov 14 '12 at 13:03
  • @JoachimPileborg: Ah yes, of course -- I actually should have known that, since I looked all that up for an answer a while ago! – Kerrek SB Nov 14 '12 at 13:06
  • 3
    This answer doesn't tell the whole story: if constructing the sentry object fails, then we don't get so far as "if extraction fails, zero is written to value". This can happen if the stream already has an error condition set, or only contains whitespace. (The sentry performs the whitespace skipping). The original value will be retained in that circumstance – M.M Sep 09 '19 at 09:17
  • E.g. for the code in the question, take out the line `ss << "The quick brown fox.";` . I mention this as some other questions which are more akin to my example have taken to linking to this answer as a reference. – M.M Sep 09 '19 at 09:26
4

The operator >> is a formatted input operator.
As such is dependent on the locale for how input is read from the stream:

[istream.formatted.arithmetic]

As in the case of the inserters, these extractors depend on the locale’s num_get<> (22.4.2.1) object to perform parsing the input stream data. These extractors behave as formatted input functions (as described in 27.7.2.2.1). After a sentry object is constructed, the conversion occurs as if performed by the following code fragment:

   typedef num_get< charT,istreambuf_iterator<charT,traits> > numget;
   iostate err = iostate::goodbit;
   use_facet< numget >(loc).get(*this, 0, *this, err, val);
   setstate(err);

As we can see above the value is actually set by the numget facet of the locale imbuded onto the stream.

num_get virtual functions [facet.num.get.virtuals]

Stage 3:

The numeric value to be stored can be one of:

  • zero, if the conversion function fails to convert the entire field. ios_base::failbit is assigned to err.
  • the most positive representable value, if the field represents a value too large positive to be represented in val. ios_base::failbit is assigned to err.
  • the most negative representable value or zero for an unsigned integer type, if the field repre- sents a value too large negative to be represented in val. ios_base::failbit is assigned to err.

The definition of stage 3 changed drastically between n2723 -> n2798

Where do I find the current C or C++ standard documents?

num_get virtual functions [facet.num.get.virtuals]

Stage 3: The result of stage 2 processing can be one of:

  • A sequence of chars has been accumulated in stage 2 that is converted (according to the rules of scanf) to a value of the type of val . This value is stored in val and ios_base::goodbit is stored in err .
  • The sequence of chars accumulated in stage 2 would have caused scanf to report an input failure. ios_base::failbit is assigned to err.
Community
  • 1
  • 1
Martin York
  • 257,169
  • 86
  • 333
  • 562