7 ms·
I'm sure this will be controversial, but my attitude towards C++ type casting is much, much simpler. 1. I only use static_cast, the other three C++-specific ca
by keldaris 6y ago
I'm sure this will be controversial, but my attitude towards C++ type casting is much, much simpler.
1. I only use static_cast, the other three C++-specific casts are, in my opinion, somewhere between useless and harmful.
2. The only meaningful reason to prefer static_cast over C-style casts is easy greppability. If you find you don't benefit from this difference, C-style casts are just fine.
These are obviously highly opinionated personal rules that serve me well in the subset of C++ I prefer to use. Your mileage can and very likely will vary substantially.
- catblast 6y agoThe other advantage other than greppabilty/documentation is that the compiler will limit what the static_cast does. Since a c style cast can be a reinterpret cast or a const cast you do lose some compiler provided protection.
- keldaris 6y agoThat's true, but there's a reason I chose not to list it - this has never actually saved me from a bug, ever. Of course, as I said, your mileage may vary.
- stinos 6y agoThe only meaningful reason to prefer static_cast over C-style casts is easy greppability. That's not just it: static_cast will also result in a compiler error when trying to cast things which don't make sense so that's extra safety.
- gok 6y agoBut it's a completely worthless compiler error. The fix is always to replace static_cast with reinterpret_cast. In 15 years of C++ I can't think of a single time that this error has actually caught a legitimate mistake.
- MauranKilom 6y ago> The fix is always to replace static_cast with reinterpret_cast. That sounds like a fast trip to aliasing violations (aka UB). There's no way you only ever casted from/to byte arrays or void* when you applied the above fix over 15 years.
- gok 6y agoI guess I had the benefit of some years of learning to smell for C aliasing violations, but even if I hadn't, static_cast would have been a dreadful way to learn. If static_cast were purely to detect pointer aliasing violations, it may have some amount of usefulness. But it also whines on totally legitimate conversions. Like both these casts: // float *x = ... static_cast<int*>(x) static_cast<intptr_t>(x) …give the exact same compiler error. And in the case of the first one, which is actually bad, the compiler can't explain why it's dangerous. Thus it doesn't actually tell me "hey you probably are casting to the wrong thing", but rather "you picked the wrong cast to make the compiler happy".
- ycombobreaker 6y agoThe static_cast vs. reinterpret_cast is also executable documentation for your readers. Sounds like you have a strong handle on this, and it's a free opportunity to make your future maintainers' lives a little bit easier.
- gok 6y agoI appreciate that in general but not here. What does reinterpret_cast<intptr_t>(p) tell a reader that (intptr_t)p doesn't?
- ycombobreaker 6y agoSince this is about maintenance, it's ultimately craftsmanship (IMHO) amd I don't think I can give a great answer without knowing real context. Here's one possibility that comes to mind: Places where I would _expect_ to see something like this are C APIs where void pointers are used to provide "userdata context", such as pthread_create. Using the reinterpret-cast-to-pointer-sized-thingie(void* or intptr_t or uintptr_t) makes it clear that we're discarding all type safety and probably doing something similar. The C style cast does not express that as clearly. Is the impact substantial? Not really. But in maintenance of long lived code bases, small impacts accumulate.
- kllrnohj 6y agoNow that `mutable` exists const_cast should indeed be avoided. But `reinterpret_cast` has quite a bit of use, particularly in FFI scenarios. For example when using JNI there is no void* type in Java, so you are forced to store the pointer in a jlong. But you also need to be super duper sure that no conversion happens, it's _just_ a really tiny 8-byte allocation. So you reinterpret_cast back & forth. My JNI code is littered with variants of reinterpret_cast<jlong>( T* ) & reinterpret_cast< T* >(jlong) - and it is neither useless nor harmful. reinterpret_cast is also quite useful when writing custom allocators. More niche usage there, of course, but still far from useless or harmful. > 2. The only meaningful reason to prefer static_cast over C-style casts is easy greppability. If you find you don't benefit from this difference, C-style casts are just fine. If you do actually think that static_cast is the only useful cast then this is super wrong advice. C-style casts are "compiler guesses if it's static_cast or reinterpret_cast, and const-ness is completely ignored." They are not a shortened alias for static_cast.
- JoeAltmaier 6y agoAgreed. But triggered by a thing written there: a pointer in a jlong? Is there any guarantee these things are the same size? Works on one platform, cool, maybe that's enough. But doesn't sound portable.
- klodolph 6y agojlong is always 64 bits. I would be interested in knowing more details about platforms where your pointers are wider than 64 bits. Not impossible, but certainly pathological.
- bregma 6y agoIt's pretty straightforward to grep for C-style casts, too. The greppability advantage falls to interpret_cast. If you have bugs, the fastest way to find them is to first grep for reinterpret_cast.
- MauranKilom 6y agostatic_cast from base to the wrong derived class is UB and can quickly become a vulnerability: https://wenke.gtisc.gatech.edu/papers/caver.pdf https://wenke.gtisc.gatech.edu/papers/caver.pdf dynamic_cast to the wrong derived class has defined behavior and is imo worth relying on when speed is simply irrelevant (e.g. once per button click). That still leaves the argument open whether casting from base to derived is ever good, see other comment trees in this thread.
- keldaris 6y agoThis is probably a reasonable point in a subset (and mindset, frankly) of C++ that has very little overlap with the subset of C++ I use. Every C++ codebase I work on has RTTI (and exceptions) disabled immediately, so dynamic_cast isn't even an option. If it was, I would consider any use of dynamic_cast to be a bug because of the egregious performance implications. The correctness concern should be addressed either at compile time (either by construction, i.e. CRTP over runtime polymorphism or by other means) or by tests. Obviously, I'm extremely biased on this, because performance is effectively the primary (and often only) reason I use C++. In other domains, YMMV.