Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Evaluating stream operator >> as boolean

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?

like image 423
acraig5075 Avatar asked Jan 10 '17 08:01

acraig5075


1 Answers

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 !.

like image 102
songyuanyao Avatar answered Oct 06 '22 22:10

songyuanyao