Overloading “operator bool” for fun and for profit
C++ is a great language when it comes to extensibility. I used to think most customizations were possible only because of the famous C preprocessor, however, other features such as operator overloading open the door for a whole new degree of extensibility too.
Operator overloading is a mechanism built right into C++ that allows classical operators such as +, *, /, – to be “overloaded” to operate on our objects. In some scenarios, operator overloading can greatly improve program readability, as it provides an infix notation for denoting operations on two objects.
This post is not about operator overloading in general, however (at least not about overloading the typical operators), but rather, about overloading the special operators we have in C++. In particular, and as you may have guessed from the post’s title, I’m interesting in talking about the “operator bool“.
operator bool is the operator that allows us to define if and how our objects can be implicitly cast into a boolean value. Why would you want to define an implicit conversion from an object into a boolean value? Well, it turns out there are a couple of cases where it can really help improve program readability.
Let’s consider the following example. Here, I want to write a program that reads a text file.
Now, there are a number of things that can go wrong when reading a file. The file might not exist. We might have read past it’s end. An error could’ve occurred. Etc. Wouldn’t it be great if we could test whether everything was OK without having to ask for each single possible error? Enter operator bool.
#include <fstream> using std::ifstream; #include <iostream> using std::cout; using std::endl; #include <string> using std::string; int main(int argc, char* argv[]) { ifstream in("f.txt"); while (in) { string word; in >> word; cout << word << " "; } cout << endl; return 0; }
Notice the loop condition. ifstream instances can be implicitly cast into a boolean value to test for non-existing files, EOF, and other read errors.
Overloading operator bool is really easy. In the next snippet I'll show you how. Imagine we have a data provider class that, er, provides some data. We want to be able to test whether the stream is valid and has data just by dropping the object reference into a conditional.
class DataProvider { public: operator bool() { return this->valid() && this->hasData(); } // Rest of class omitted... }; // somewhere down the road... void process(const DataProvider& dp) { while (dp) { unsigned char* pdata = dp.getData(); // ... } }
Easy, huh? One area where operator bool really shines is writing a custom smart pointer implementation. Operator bool can allow your library's users to test whether a smart pointer is null just by implicitly casting the smart pointer instance to bool.
Have a look at the following example. This is something we're doing today with Algorithmia's Vortex-Engine.
void renderQuad() { ref_ptr<Texture> pTexture = getTexture("quad_texture.png"); if (pTexture) { // Use texture for rendering a quad } else { // Error loading texture! } }
In conclusion, C++ operator overloading is a powerful feature that adds a great deal of flexibility to the language. Nothing prevents you from redefining and completely changing the meaning of the operators that work on your objects. It's the programmer's responsibility to determine whether overloading an operator makes sense and actually improves readability, instead of tampering with it.
In general, use common sense and remember that with great power comes great responsibility.