Оценка оператора потока >> как булева

Следующий код компилируется в Visual Studio 2008, но не работает в Visual Studio 2013 и более поздних версиях.

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

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

Сообщение об ошибке

ошибка C2678: двоичный '==': оператор не найден, который принимает левый операнд типа 'std:: basic_istream > ' (или нет приемлемого преобразования)

и успешно фиксируется путем изменения следующим образом:

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

Я не понимаю этого хорошо. Мой вопрос заключается в том, какие флаги оператора или cast или, возможно, ios, которые позволяют считать чтение потока как булево в первую очередь, а затем почему недостаток operator== нарушает его?

Ответ 1

Два поведения изменились с С++ 11.

  • Изменено поведение std:: basic_ios:: operator bool.

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

    Обратите внимание, что С++ 11 operator bool() объявляется как explicit; но для if ((ss >> f) == false), ss (т.е. возвращаемое значение (ss >> f)) должно быть неявным преобразованным в bool (для сравнения с false), что недопустимо.

  • Изменено определение константы нулевого указателя.

    До того, как С++ 11 operator void*() можно было бы использовать, а это не explicit (до С++ 11 нет такого явного пользовательского преобразования), а до С++ 11 константа нулевого указателя определяется как:

    интегральное постоянное выражение rvalue целочисленного типа, которое вычисляется до нуля (до С++ 11)

    что означает, что false может использоваться как константа нулевого указателя. Таким образом, ss может быть неявно преобразован в void*, а затем сравниваться с false (как нулевой указатель).

    Из С++ 11 константа нулевого указателя определяется как:

    целочисленный литерал со значением 0 или значение типа std::nullptr_t(поскольку С++ 11)

    пока false снова не будет; это не целочисленный литерал.

Итак, из-за этих двух изменений if ((ss >> f) == false) не будет работать в С++ 11 и более поздних версиях.

С другой стороны, if (!(ss >> f)) работает отлично, потому что std:: basic_ios:: operator! (как до, так и после C + +11).

bool operator!() const;

Возвращает true, если в связанном потоке произошла ошибка. В частности, возвращает true, если badbit или failbit установлены в rdstate().

BTW: Поскольку С++ 11, даже без std::basic_ios::operator!, explicit operator bool() const также мог бы сделать if (!(ss >> f)) хорошо, потому что в контексте контекстное преобразование, explicit учитывается пользовательское преобразование; т.е. ss может быть контекстно преобразован в bool для operators !.