8

Take this toy code:

#include <iostream>
#include <fstream>

int main() {

    std::ifstream is;
    // perform read
    // ...
    if (!is) // works
        std::cout << "fail";
    if( is == false)  // error C2678
        std::cout << "fail";

    return 0;
}

You'd get the following counter-intuitive results: if(!is) compiles, and if(is==false) gives

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

(for VS2015 - similar errors in gcc and clang).

The standard says (according to this answer):

Valid C++ 2003 code that relies on implicit boolean conversions will fail to compile with this International Standard. Such conversions occur in the following conditions:

  • passing a value to a function that takes an argument of type bool;

  • using operator== to compare to false or true;

  • returning a value from a function with a return type of bool;

  • initializing members of type bool via aggregate initialization;

  • initializing a const bool& which would bind to a temporary.

As far as I can tell if(is==false) is explicitly required to fail, but how come if(!is) doesn't? Doesn't it qualify as an 'implicit boolean conversion'?

Was this conversion-to-bool deliberately omitted from the cases listed in the standard? Perhaps it's an unintentional omission?


Edit: This code fails just as well:

int main() {

    std::ifstream is;
    // perform read
    // ...
    if (is) // works
        std::cout << "success";
    if( is == true)  // error C2678
        std::cout << "success";

    return 0;
}

And here the presence of operator!() is irrelevant.

Community
  • 1
  • 1
Ofek Shilon
  • 14,734
  • 5
  • 67
  • 101

2 Answers2

9

std::ifstream's inherited operator bool is marked explicit:

explicit operator bool() const; (2) (since C++11)

What this means is that there is no implicit conversion to bool, i.e. all of those expressions fail:

bool result = is;    // fails
bool b = is == true; // fails
if (is == false);    // fails
while (is == false); // fails

If you wonder why if (is) and similar statements compile, that is because there are special rules for if, while, and the like for conversions: The explicit keyword is ignored!

if (is);                        // ok
while (is)                      // ok
bool b = static_cast<bool>(is); // ok

Note that the last case compiles because you are explicitly wanting a bool.

Technically, !is would work fine, as you explicitly want a bool, but std::ifstream has a inherited operator!, so that operator is called instead of the default operator! which operates on bools:

if (!is);     // ok
bool b = !is; // ok
Rakete1111
  • 47,013
  • 16
  • 123
  • 162
  • 2
    It could [potentially work](http://coliru.stacked-crooked.com/a/b5f1f0d91368afc3) without the explicit `operator!` – StoryTeller - Unslander Monica Nov 27 '16 at 12:15
  • I also think it could work just like `is && true` does – W.F. Nov 27 '16 at 12:17
  • @Rakete1111 Thanks. Can you quote the standard on this? Do you have any insight into *why* if/while clauses ignore the explicit keyword? It certainly makes it more confusing (for me at least). – Ofek Shilon Nov 27 '16 at 12:28
  • 4
    @OfekShilon look up [*contextual conversion to bool*](http://en.cppreference.com/w/cpp/language/implicit_conversion#Contextual_conversions). That is the context of the controlling expression of `if` and so on. Explicit operator bool is considered in those contexts. – M.M Nov 27 '16 at 12:30
4

!is works because std::basic_ios, one of the base classes for the streams, has an operator!() defined.

So this is not a conversion at all, it is a call to is.operator!().

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
  • You're right, but this isn't the root cause. I've edited the question to reflect it: `if(is)` succeeds just as `if(!is)` – Ofek Shilon Nov 27 '16 at 12:23
  • Yes, because `std::basic_ios` also has an `operator bool()` that complements the `operator!()`. – Bo Persson Nov 27 '16 at 12:35
  • The operator is marked `explicit`, which is the source of the confusion. Turns out the explicit qualifier is ignored in this context – Ofek Shilon Nov 27 '16 at 21:01
  • No, it's not. This is exactly the place where an `explicit operator bool()` is designed to work: "Certain language constructs require that an expression be converted to a Boolean value. An expression `e` appearing in such a context is said to be *contextually converted to bool* and is well-formed if and only if the declaration `bool t(e);` is well-formed, for some invented temporary variable `t`" – Bo Persson Nov 27 '16 at 21:55
  • `explicit` isn't mentioned there. Am I missing something? – Ofek Shilon Nov 28 '16 at 07:19
  • The key here is `bool t(e);` which is an explicit conversion to `bool`. The guys writing the quoted phrase know that, and assume that we also have a holistic view of the standard - you have to read *all* of it and realize what parts affect other parts. – Bo Persson Nov 28 '16 at 10:13