C++ tidbit #3: Contextual Conversion
Warmup: explicit constructors
When you mark a cast operator as explicit:
struct C {
explicit operator bool() { return true; }
};
(yes, that is a silly implementation) you're saying that implicit casts will fail to compile:
bool b1 = c; // error
bool b2 = c==true; // error
bool b3 = static_cast<bool>(c); // explicit cast - success
This is generally considered a good practice, as implicit casts are a gaping hole in C++ strong-type system. If, for example, you passed a wrong argument to a function - you want the compiler to err and help you catch it, and not implicit-cast to hide away the source of the error and sneak up on you in surprising ways later.
Some Surprises
So, what happened in the 2nd line here?
if (c == true) {} // compilation error, as expected
if (c) {} // success ?!
And here??
while(c) {} // success !?
for (int i=0; c ; ++i) {} // success !?!
And, oh my, here?
bool b4 = c || false // success !!?
bool b5 = c ? true : false; // success !??!
No explicit casts are anywhere to be seen. Shouldn't all these implicit casts be forbidden??
You can fiddle with this live code in various compilers here.
Enter Contextual Conversion
`bool` is special.
There are specific locations within the C++ syntax that require a bool type. In all the surprise-success cases above, `c` appears in such a place (go check!).
In these places a slightly different set of rules kicks in, and the term employed is contextual conversion (not casting!) to bool. In a nutshell: existing cast-operators to bool are considered, with `explicit` ignored. Details and some delicate diffs between C++ versions here.
Final Musing
This is a fine example of a good intention on the road to hell. Sometime during C++'s evolution, its designers wished to provide this convenience to developers - and not have them type out casts to bool where it would seem clear from the context that such a cast must be present.
I would happily trade the extra head-scratches this had caused me for some extra typing and reading. The same goes for many, many other C++ dark corners too. Some modern languages (notably Rust) learnt that lesson well, and allow no implicit casting of any kind.
Signal Processing and Software Engineer | Motivated by creating value for the end user | Interested in fault tolerance and flexibility of software | Keen language learner and language transmission activist
2 年OMG! The horror! Thanks for highlighting this nuance(?) (read: nuisance?)