13

The following code compiles in Visual Studio 2008 but fails in Visual Studio 2013 and later.

std::string str("foo");
std::stringstream ss(str);
float f = 0;

if ((ss >> f) == false)
    std::cout << "Parse error\n";

The error message is

error C2678: binary '==' : no operator found which takes a left-hand operand of type 'std::basic_istream>' (or there is no acceptable conversion)

and is successfully fixed by changing as follows:

if (!(ss >> f))
    std::cout << "Parse error\n";

I'm not understanding this well. My question is, what operator or cast or maybe ios flags are involved that allow the stream read to be evaluated as a boolean in the first place, and then why does the lack of an operator== break it?

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
acraig5075
  • 10,588
  • 3
  • 31
  • 50

1 Answers1

16

Two behaviors changed since C++11.

  1. The behavior of std::basic_ios::operator bool changed.

    operator void*() const;         (1) (until C++11)
    explicit operator bool() const; (2) (since C++11)
    

    Note since C++11 operator bool() is declared as explicit; but for if ((ss >> f) == false), ss (i.e. the return value of (ss >> f)) needs to be implicit converted to bool (to be compared with false), which is not allowed.

  2. The definition of the null pointer constant changed.

    Before C++11 operator void*() could be used and it's not explicit (before C++11 there's no such explicit user-defined conversion), and before C++11 the null pointer constant is defined as:

    an integral constant expression rvalue of integer type that evaluates to zero (until C++11)

    which means false could be used as a null pointer constant. So ss could be implicitly converted to void* and then compared with false (as the null pointer).

    From C++11, the null pointer constant is defined as:

    an integer literal with value zero, or a prvalue of type std::nullptr_t (since C++11)

    while false is not again; it's not an integer literal.

So, because of these two changes, if ((ss >> f) == false) won't work in C++11 and later.

On the other hand, if (!(ss >> f)) works fine because there's std::basic_ios::operator! (both before and after C++11) for it.

bool operator!() const;

Returns true if an error has occurred on the associated stream. Specifically, returns true if badbit or failbit is set in rdstate().

BTW: Since C++11, even without std::basic_ios::operator!, explicit operator bool() const could also make if (!(ss >> f)) works well, because in the context of contextual conversion, explicit user-defined conversion is considered; i.e. ss could be contextually converted to bool for operators !.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • Something worth mentioning is that `if(foo)` or `if(!foo)` works even if `operator bool` is marked `explicit` due to one of the few exception regarding explicit conversions for `bool` operators. Example [here](http://coliru.stacked-crooked.com/a/e884a7adaf92a472). I believe this is called **contextual conversion**, see e.g. [this blog post](http://chris-sharpe.blogspot.ca/2013/07/contextually-converted-to-bool.html). – vsoftco Jan 10 '17 at 15:42
  • 1
    `NULL` is a macro that expands to *a* null pointer constant, not "the" null pointer constant, nor a synonym for the term "null pointer constant". – T.C. Jan 10 '17 at 15:54