Recently I noticed my knowledge about C++ cast operators had been far from complete. For instance, I had thought static_cast is only for static upcasts(or downcasts also?) in an inheritance hierarchy. And for some legitimate casts between primitive types(like between int and float or between int and unsigned int), I used the C-style cast, still. I was not sure about how new cast operators are precisely mapped to the old C-style, either. So here is my shot at finally clarifying all these.
Categories
Errors possible
- Es : compile error
- Er: run-time check failure
- an exception when casting a reference
- returing nullptr when casting a pointer
- Ec : run-time crash when using the converted
Conversions supported
- Cprim: between primitive types : int <-> float, int <-> unsigned int
- built-in conversion rules apply
- pointer(reference) types
- Cbase: pointer to a linearly related type(upcast/downcast) : CDerived* <-> CBased*
- a proper pointer arithmetic applies if a multiple inheritance used
- Cvoid : void pointer : void* <-> int*
- Cconst : removing const/volatile type qualifiers : const int* <-> int*
- Cetc : Any other cases : int* <-> float*
- Cbase: pointer to a linearly related type(upcast/downcast) : CDerived* <-> CBased*
Casts
static_cast<>
- Cprim, Cbase, Cvoid
- Es if Cconst, Cetc tried
- A possible Ec if an invalid downcast tried
e.g. CBase* pB = new CBase(); CDerived* pD = static_cast<CDerived*>(pB);
reinterpret_cast<>
- Cbase, Cvoid, Cetc
- Es if Cprim, Cconst tried
- A possible Ec if Cbase tried(because a proper point arithmetic isn’t applied)
dynamic_cast<>
- Cbase, dynamic_cast<void*>
- The latter returns a pointer to the complete(i.e. most-dervied) object.
- Es if Cprim, Cvoid, Cconst, Cetc
- A possible Er if a downcast of Cbase tried and its run-time check fails
const_cast<>
- Cconst
- Es if Cprim, Cbase, Cvoid, Cetc
C-style cast
- A C-style cast is defined as the first of the following which succeeds:
- const_cast
- static_cast
- static_cast, then const_cast
- reinterpret_cast
- reinterpret_cast, then const_cast
Conclusion
- Never use the C-style cast any more. You can use one among (or an combo of) const_cast, static_cast, reinterpret_cast depending on your exact need at that time.
- dynamic_cast is a complete new thing in C++ and use it sparingly since it has a run-time cost and a need for it might be a symptom of some bad underlying design(though this aspect of it has not been discussed in this article).
- C++ is too deep (for a mere mortal to figure all its nooks and crannies).
References
(This article has also been posted to my personal blog.)
Update – dynamic_cast<void*> and the fourth reference added