I found myself in a situation where I know what type something is. The Type is one of three (or more) levels of inheritance. I call factory which returns B*
however T is either the highest level of a type (if my code knows what it is) or the 2nd level.
Anyways, I did a static_cast
in the template which is the wrong thing to do. My question is WHEN can I static cast safely? Is there ever such a time? I did it in this case because I'd rather get compile errors when I accidentally have T as something wacky which (has happened and) dynamic cast ignores (and returns null). However when I know the correct type the pointer is not adjusted causing me to have a bad pointer. I'm not sure why static cast is allowed in this case at all.
When can I use static_cast for down casting safely? Is there ever a situation? Now it seems like it always is wrong to use a static_cast
(when the purpose is to down cast)
Ok I figured out how to reproduce it.
#include <iostream>
struct B { virtual void f1(){} };
struct D1 : B {int a;};
struct D2 : B {int a, b; };
struct DD : D1, D2 {};
int main(){
void* cptr = new DD(); //i pass it through a C interface :(
B* a = (B*)cptr;
D2* b = static_cast<D2*>(a); //incorrect ptr
D2* c = dynamic_cast<D2*>(a); //correct ptr
std::cout << a << " " <<b << " " <<c;
}
A cross cast doesn't need a dynamic_cast at all..
If
Derived
hasBase
as a public (or otherwise accessible) base class, andd
is of typeDerived*
, thenstatic_cast<Base*>(d)
is an upcast.This is always technically safe.
And generally unnecessary, except for cases where you have hiding (shadowing) of method.
Cheers & hth.,
The problem lies with this line:
If you convert something to a void pointer, you must convert it back to the same type that it was converted from first before doing any other casts. If you have a situation where multiple different types of objects have to go through the same void pointer, then you need to first cast it down to a common type before converting to a void pointer.
EDIT: If you only know that cptr points to some object which is of a type derived from B at the time of the cast, then that isn't enough information to go on. The compiler lets you know that when you try to convert the DD pointer to a B pointer.
What you would have to do is something like this:
but I'm not sure if that is acceptable in your actual usage.
Just for completeness (knowing that I'm late a little, just for late readers like me...):
static_cast
can be applied, if used correctly!At first, the simple case:
You can get from
D1*
toD2*
via intermediate downcast toDD*
:The upcast to D2* is implicit then. This is possible even for non-virtual inheritance. But be aware that you need to be 100% sure that d1 really was created as
DD
when doing the downcast, otherwise you end up in undefined behaviour!Now the more complex case: Diamond pattern! This is what is presented in the question:
Now this cast is already is dangerous! What actually is implemented here is a reinterpret_cast:
What you instead want is a simple upcast. Normally, one would not need a cast at all:
Solely: DD has two inherited instances of B. It would have worked if both D1 and D2 inherited virtually from B (
struct D1/2 : virtual B { };
– not to be confused with B/D1/D2 being virtual classes!).The cast to the respective bases
D1
orD2
now makes clear which of the two inherited instances ofB
shall be pointed to.You now can get back the respective other instance by downcasting to DD again; due to the diamond pattern, you need an intermediate cast again:
The very important point about all this matter is: You absolutely need use, when down-casting, the same diamond edge you used for up-casting!!!
Conclusion: Yes, it is possible using static casts, and it is the only option if the classes involved are not virtual (note: to be differed from virtual inheritance!). But it is just too easy to fail in doing it correctly, sometimes even impossible (e. g. if having stored pointers of base type to arbitrary derived types in a
std::vector
), so usually, thedynamic_cast
solution as presented by Ben is the much safer one (provided virtual data types are available; if so, in the forementioned vector example, it is the only solution!).A cross-cast:
requires use of
dynamic_cast
, it cannot be done withstatic_cast
(static_cast
should have caused a compile-time error).dynamic_cast
will also fail if either base class is not polymorphic (the presence of virtual functions is NOT optional).See this explanation on MSDN
You can safely upcast if you are sure that the object is actually an instance of that class.