OR: What do you get if you convert an I/O stream to a void *?

A neat feature of the C++ I/O library is the error handling provided by iostreams and friends. Any fstream has a fail() method, which says if something's wrong. So to read an int from std::cin while checking errors, just say

int i;
std::cin >> i;
if (std::cin.fail()) {
  // something's wrong...
}
Since you usually want a readable way to check for errors, the language lets you use the stream itself as a boolean test:
int i;
std::cin >> i;
if (! std::cin) {
  // something's wrong...
}
Since the >> input operator returns its LHS as an lvalue (this also lets you write "std::cin >> i >> s"; see C++: why input is >> and output is << for the gory details), you can just say
?
int i;
if (! (std::cin >> i)) {
  // something's wrong...
}
which is very elegant.

Design and Implementation

How is all this implemented? The obvious way would be a conversion:

class my_istream {      // templates, inheritance left out
  // ...
public:
  // ...
  operator bool() const;
  // ...
};
Presumably defining the operator by saying something like
my_istream::operator bool() const { return ! fail(); }

This is enough to make all the above work. But it's also enough to defeat almost any type checking the C++ compiler might try to do. Assuming my_istream a, b;, all of these are legal with the above declaration:

  • a+b;
  • a%b;
  • a==b+1 (same as saying !a.fail() && b.fail()!);
  • a+b < 2 (same as saying a.fail() || b.fail()!)
  • 17 << a (NOT the same as a >> 17!).
bools are just integer types, and integer types have very "convenient" arithmetic defined for them. But it's all nonsense for a file stream.

By contrast, about the only arithmetic that is legal for a C stdio.h FILE * is adding or subtracting an int (and even that can easily be prevented, by keeping FILE an undeclared struct in the header file. This too is unpleasant, but surely C++ should be more type safe than C!?

A "better"(?) way

C++ avoids the above unpleasantries, by not defining a bool conversion. Instead, to test a file stream for errors, the equivalent of the following is used:

class my_istream {      // templates, inheritance left out
  // ...
public:
  // ...
  operator void*() const;
  // ...
};
// ... and later...
my_istream::operator void*() const { return fail() ? 0 : this; }
C++ doesn't have any implicit conversions from void * to other pointer types, so code like int *i = a; remains illegal. You cannot perform arithmetic on 2 void pointers, so a+b and friends are illegal. You cannot do pointer arithmetic, either, so a+2 is also right out (this is why the conversion is to void *, not to my_istream *).

About the only things you can do with the implicitly-converted void pointer are test for truth and falsehood, by saying "if (a) ..." and "if (! a) ...". This is exactly what was desired above.

But who would have guessed that the correct way to convert to bool is to convert instead to void *?