Casting in C++ is a confusing jumble of unclear and dangerous operations. It mixes unrelated concepts. It introduces ambiguities and redundancies. It’s an essential but flawed aspect of the language.
(This article is part of the series on Defective C++)
A Broken Any Cast
C++ inherited the casting syntax from C. For compatibility reasons, one presumes, the C-style cast is equivalent to either a const_cast, static_cast, reinterpret_cast or combination thereof. Having const_cast and reinterpret_cast in the list make the language dangerous. Like C, the cast notation can convert from any arbitrary pointer type to any other arbitrary pointer type whether they are in any way compatible or not.
This isn’t an argument against the various forms of casting. Each have their own specific purpose and are valuable in their own way. static_cast and dynamic_cast are by far the most commonly used and fulfil most intended meanings of cast. const_cast is frequently used, and though needed, does introduce a potential for invalid code. reinterpret_cast is by far the least used, and the most likely to do the wrong thing. So the failure is that the simplest cast notation can actually invoke the least used and most dangerous casting operation.
Part of the problem is the mixing of casting and conversion. If you wish to convert one type to another type, whether a fundamental or object, you can use a static_cast. This is very bad, since logically a conversion is not the same as a cast: a cast results in a different view on the original object, and a conversion results in a new temporary object. Equating these two meanings just muddles the meaning of both of them. Given that the functional notation also exists for type conversion it is not clear why static_cast needs to do the same thing.
A saner option would be to do a static_cast or dynamic_cast. Perhaps the sanest option would be to do a dynamic_cast and throw an exception if it could not be converted. Usually when you cast it is because you expect to have the desired type and less frequently because you are checking the type. Thus an exception makes sense here. A distinct operation for type checking is a good option instead of an attempted cast.
For situations where performance is important, static_cast would still be available; dynamic cast is a relatively slow operation. The same rules would apply: you can only static_cast between types part of the same class hierarchy.
Conversion would be done strictly via a conversion syntax and would be unrelated from casting. How conversion is done also has issues, but we can get to those another time.
reinterpret_cast is a difficult beast to approach. It has several very distinct purposes. Ideally a language would have another way to approach each of its uses without having to introduce this very dangerous operator. Exactly how is cause for another article.
Categories: Defective Language