15

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;
}
Null
  • 1,950
  • 9
  • 30
  • 33
  • Please be more specific. Can you provide a code example? I think I know roughly what you are saying, but I'm not sure. In general it is "safe" to do a static_cast for upcasting if you are casting to the proper type. – Vaughn Cato Oct 17 '11 at 04:26
  • @Vaughn: I cant really show the code (its a problem i ran into at work) but one of the problems is multiple inheritances and using classes as an interface –  Oct 17 '11 at 04:35
  • 3
    Required reading for everyone using the word "upcast": [An upcast is a cast from a derived type to one of its base classes](http://msdn.microsoft.com/en-us/library/1bh3t52k(v=vs.80).aspx) – Ben Voigt Oct 17 '11 at 04:41
  • Can you give a code example that demonstrates the issue though? Something new that you come up with just for demonstration. – Vaughn Cato Oct 17 '11 at 04:44
  • Ouch, this looks like a case for virtual inheritance. This almost always gets really messy and is almost always avoidable - are you sure you really need this? Eg. instead of DD inheriting from D1 and D2, why not use composition? (`struct DD : B {D1 d1; D2 d2;};`) – Ayjay Oct 17 '11 at 05:11
  • @acidzombie24: That helped a lot. My answer below. – Vaughn Cato Oct 17 '11 at 05:11

6 Answers6

15

A cross-cast:

struct Base1 { virtual void f1(); };
struct Base2 { virtual void f2(); };
struct Derived : Base1, Base2 {};

Base1* b1 = new Derived();
Base2* b2 = dynamic_cast<Base2*>(b1);

requires use of dynamic_cast, it cannot be done with static_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

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • ah ha +1. I'd like to mention in my situation it didnt cause a compile error sadly. –  Oct 17 '11 at 04:48
  • I'll just add as long as base hasnt been converted to a void pointer nor reinterpret_cast has been use which causes pointer adjustment confusion especially when there are ambiguity. Then static_cast is safe to use. Assuming there is compile error and the type being cast to is indeed the correct type –  Oct 17 '11 at 07:48
  • 1
    @acidzombie24: No, `static_cast` is not safe to use when casting with multiple inheritance, even if from `void*`. – Ben Voigt Oct 17 '11 at 14:00
  • Why is `static_cast` unsafe when casting with multiple inheritance? I thought doing `Base2* b2 = static_cast(b1)` is safe, given we're sure `b1` is-a `Derived`. Confused. – legends2k Aug 03 '20 at 11:15
  • 1
    @legends2k: You didn't do the cross-cast. A cross-cast doesn't know which `Derived` type is involved, as long as the object has *some* type that inherits both `Base1` and `Base2`. – Ben Voigt Aug 03 '20 at 15:26
  • @legends2k: See [my original comment on the distinction](https://stackoverflow.com/questions/7789326/when-is-static-cast-safe-when-you-are-using-multiple-inheritance/7789468?noredirect=1#comment51095956_30759488) – Ben Voigt Aug 03 '20 at 15:30
  • Thanks! I get it now. With `static_cast` though we achieve cross cast by down casting and (implicit) up casting, it isn't truly a cross cast. For cross cast one needs to use `dynamic_cast` and do the conversion directly from `Base1*` to `Base2*`. This can't be done with `static_cast`. – legends2k Aug 03 '20 at 21:38
5

If Derived has Base as a public (or otherwise accessible) base class, and d is of type Derived*, then static_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.,

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • Wrong. When using multiple inheritance D1 and D2 can inherit from base. Your condition would be true but a static cast to Dn can be incorrect and was the problem –  Oct 17 '11 at 04:31
  • It's always unnecessary to use a cast to upcast, this conversion will take place implicitly where needed. – Ben Voigt Oct 17 '11 at 04:34
  • @acidzombie24: What you're describing is not an upcast, it's a cross-cast. And totally unrelated to the question, AFAICT. – Ben Voigt Oct 17 '11 at 04:35
  • @Ben interesting. I never heard of 'cross cast' –  Oct 17 '11 at 04:36
  • 2
    @acidzombie24: Sorry, what you described isn't a cross-cast. A cross-cast is when you `dynamic_cast` from `Base1` to `Base2`, given an object that actually inherits both `Base1` and `Base2`, but `Base1` and `Base2` are themselves unrelated. But Alf is absolutely correct about upcasting. I don't think you meant to ask about upcasting, but rather downcasting. – Ben Voigt Oct 17 '11 at 04:39
  • @Ben: hmm both of my 'base' do share a different a different base. But assuming the class has multiple inheritance it is never safe? –  Oct 17 '11 at 04:42
  • 2
    @acidzombie24: Further reading of your question suggests that you might be talking about cross-casting (effectively a down-cast followed by an upcast) and just didn't explain it well. You really need to provide at least a distilled example of what you mean, even if it isn't your actual code. – Ben Voigt Oct 17 '11 at 04:46
  • 1
    When I answered this question the title was "When is static cast safe for upcasting?", and there was no code. I'm not interested in chasing SO history changes... – Cheers and hth. - Alf Oct 17 '11 at 06:16
  • @anonymous driveby downvoter: explain your downvote so that others can benefit from your insight. – Cheers and hth. - Alf Oct 17 '11 at 10:15
3

The problem lies with this line:

B*  a = (B*)cptr;

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.

int main(){
  B *bptr = new DD; // convert to common base first (won't compile in this case)
  void* cptr = bptr; // now pass it around as a void pointer
  B*  a = (B*)cptr; // now back to the type it was converted from
  D2* b = static_cast<D2*>(a); // this should be ok now
  D2* c = dynamic_cast<D2*>(a);  // as well as this
  std::cout << a << " " <<b << " " <<c;
}

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:

int main(){
  void* cptr = new DD; // convert to void *
  DD* a = (DD*)cptr; // now back to the type it was converted from
  D2* b = static_cast<D2*>(a); // this should be ok now, but the cast is unnecessary
  D2* c = dynamic_cast<D2*>(a);  // as well as this
  std::cout << a << " " <<b << " " <<c;
}

but I'm not sure if that is acceptable in your actual usage.

Vaughn Cato
  • 63,448
  • 5
  • 82
  • 132
  • Did you run your code? new DD will actually cause a compile error stating its ambiguous. I am positive b will still be an incorrect pointer since it doesnt know it must adjust (it doesn't recognize the actual type thus known D1 comes before D2) –  Oct 17 '11 at 05:20
  • @acidzombie24: No I didn't, and you're right. What you are trying to do isn't going to work, and the compiler is letting you know that. – Vaughn Cato Oct 17 '11 at 05:22
  • I've added a little more to hopefully give something useful. – Vaughn Cato Oct 17 '11 at 05:34
  • I dont know the actual type (thus casting to DD isnt an option) but the question is when is static_cast safe to use. It definitely isnt in my case. All i figured out is not when there is Multiple inheritance and only if i know what the actual derived type is. Which is almost never. –  Oct 17 '11 at 05:40
  • 3
    It isn't so much that the static_cast isn't safe, it is the casting to a void pointer and then back to a different type that isn't safe. If the void pointer wasn't involved, it wouldn't be an issue. – Vaughn Cato Oct 17 '11 at 05:45
  • thats only because you cant convert that type w/o using a void pointer (or using a reinterpret_cast). I guess its safe as long as your source ptr is possible to get to w/o the use of the two above. hmmmm...... I havent said it but +1 –  Oct 17 '11 at 06:49
2

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:

struct D1 { }; // note: no common base class B!
struct D2 { };
struct DD : D1, D2 { };

You can get from D1* to D2* via intermediate downcast to DD*:

D1* d1 = new DD();
D2* d2 = static_cast<DD*>(d1);

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:

void* cptr = new DD();
B* a = (B*)cptr;

Now this cast is already is dangerous! What actually is implemented here is a reinterpret_cast:

B* a = reinterpret_cast<B*>(cptr);

What you instead want is a simple upcast. Normally, one would not need a cast at all:

B* a = new DD(); //ambigous!

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!).

B* b1 = static_cast<D1*>(new DD());
B* b2 = static_cast<D2*>(new DD());

The cast to the respective bases D1 or D2 now makes clear which of the two inherited instances of B shall be pointed to.

You now can get back the respective other instance by down-casting to DD again; due to the diamond pattern, you need an intermediate cast again:

D2* bb1 = static_cast<DD*>(static_cast<D1*>(b1));
D1* bb2 = static_cast<DD*>(static_cast<D2*>(b2));

The very important point about all this matter is: You absolutely need to 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, the dynamic_cast solution as presented by Ben is the much safer one (provided virtual data types are available; if so, in the fore-mentioned vector example, it is the only solution!).

Aconcagua
  • 24,880
  • 4
  • 34
  • 59
2

You can safely upcast if you are sure that the object is actually an instance of that class.

class Base {};
class Derived1 : public Base {};
class Derived2 : public Base {};

int main()
{
    Base* b = new Derived1;

    Derived1* d1 = static_cast<Derived1*>(b); // OK
    Derived2* d2 = static_cast<Derived2*>(b); // Run-time error - d isn't an instance of Derived2
}
Ayjay
  • 3,413
  • 15
  • 20
  • What i said to Alf. Even if your case is true (and is true for my scenario) it still leads to problems (consider when using multiple inheritances and classes being used as 'interface') –  Oct 17 '11 at 04:33
  • 2
    The upcast is `Base* d = new Derived1;`, and it's impossible for a `Derived1` to not be an instance of `Base`. – Ben Voigt Oct 17 '11 at 04:36
  • Gah I always get up and downcast confused. I don't really consider an upcast very similar to the other casts anyway. – Ayjay Oct 17 '11 at 05:00
  • @acidzombie24: What problems? – Ayjay Oct 17 '11 at 05:00
1

A cross cast doesn't need a dynamic_cast at all..

   struct Base1 { virtual void f1(); };
   struct Base2 { virtual void f2(); };
   struct Derived : Base1, Base2 {};

   Base1* b1 = new Derived();

   // To cast it to a base2 *, cast it first to a derived *
   Derived *d = static_cast<Derived *>(b1);
   Base2 *b2 = static_cast<Base2 *>(d);
Al227
  • 151
  • 7
  • 5
    A downcast followed by an upcast can be done with `static_cast` (and the second `static_cast` should simply be left out), but a direct cross-cast cannot. – Ben Voigt Jul 22 '15 at 17:56