3

This question is about which C++ style cast should be used to make this conversion. I am aware that a C style cast can achieve this.

For the following class structure:

class Foo {};

class Bar : public Foo {};

Say that I am given: Foo* ptr; and I want to cast it to a Bar* which type of cast should I be using? It seems like I must use dynamic_cast as it is:

Used for conversion of polymorphic types

I wanted to avoid dynamic_cast since it is a run time cast.

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • This is something you should avoid, doing these kind of things are sign of design issues. If you think you really need this, you can compare the typeid-s of the classes, and if it matches you can static_cast the pointer... it's way faster than a dynamic_cast, but won't work if you don't know the exact destination class. – Melkon Sep 10 '15 at 16:00
  • @Melkon how is checking typeid faster than dynamic_cast? Which compiler and settings did you see this on? – Mark Ransom Sep 10 '15 at 16:01
  • 1
    @MarkRansom: typeid check won't traverse the class hierarchy. – Melkon Sep 10 '15 at 16:02
  • 3
    Is the missing `public` a cosmetic issue or intended? If it is intended that changes things. – nwp Sep 10 '15 at 16:03
  • @Melkon - I don't see why dynamic_cast would need to _traverse the class hierarchy_ either, except with virtual inheritance – Useless Sep 10 '15 at 16:12
  • 1
    @Useless: http://stackoverflow.com/questions/579887/how-expensive-is-rtti worth to check Izhaki-s answer. dynamic_cast doesn't just check the destination class, but all class derive from it also. I think most time when people write dynamic_cast typeid check would be better. Sometimes typeid check is not enough. – Melkon Sep 10 '15 at 16:15
  • 1
    That's an invalid benchmark (as pointed out in the comments) of a 3-year-old compiler's implementation of dynamic casting between classes whose code I don't have (and which may use virtual inheritance for all I know). I wouldn't take that answer as proving anything except profiling is hard. – Useless Sep 10 '15 at 16:54
  • @nwp The access-specifier didn't matter either way so I just left it as default (`private`): http://stackoverflow.com/a/3811480/2642059 – Jonathan Mee Sep 10 '15 at 17:05
  • 1
    Pretty sure [it matters](http://ideone.com/Mn2Anf). You cannot get a valid `Foo *` from a `Bar` and therefore there is no way to `dynamic_cast` that back to a `Foo *`. – nwp Sep 10 '15 at 17:24
  • @nwp Ugh. I hate being wrong. Excellent call I have edited. – Jonathan Mee Sep 10 '15 at 17:35

3 Answers3

6

You are correct that dynamic_cast is usually the most appropriate for this situation. However, if you know that the pointer is actually pointing to an object of the derived class, you can use static_cast for the conversion. If you're wrong and the pointer is not the derived class, you'll get undefined behavior.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • I do actually know that `ptr` is-a `Bar*`. So I'm assuming you're referencing the 2nd type of conversion in this list: http://en.cppreference.com/w/cpp/language/static_cast#Explanation – Jonathan Mee Sep 10 '15 at 17:16
  • @JonathanMee yes, that's what I'm talking about - although I don't think it's necessary for the base class to be non-virtual. – Mark Ransom Sep 10 '15 at 18:56
  • @JonathanMee did you know cppreference.com is a wiki? There's no guarantee that the person who added that subject knew what they were talking about. The only definitive word is the standard itself. – Mark Ransom Sep 10 '15 at 19:21
  • I did know it was a wiki in fact. Sorry for my sarcasm I forget that it does not translate well over the interwebs. Unfortunately I can't seem to find anything more specific than http://en.cppreference.com 's description of `static_cast` Though your statement does seem to be supported by http://www.ideone.com : http://ideone.com/X0MNAS – Jonathan Mee Sep 10 '15 at 19:29
  • After looking at this further, I believe your statement to be right, in spite of what is said by http://en.cppreference.com. However I'd feel more comfortable accepting if you could provide another credible source indicating that the `static_cast` will work even if the class is `virtual`. – Jonathan Mee Sep 14 '15 at 14:28
1

static_cast will work fine so long as you are sure that the object you are casting really is the type you expect it to be. Based on the example you gave, it looks like you are sure.

Logicrat
  • 4,438
  • 16
  • 22
1

For the sake of clarity:

I wanted to avoid dynamic_cast since it is a run time cast.

Well, you have a run-time type (given a statically-typed reference to the base-class, you can't generally know the dynamic type of the object), so a run-time cast is the only wholly safe option.

If you thought your object was really a Bar, but were mistaken, dynamic_cast<Bar*> will give you a nullptr, or dynamic_cast<Bar&> will throw an exception. Either way, you have a chance to deal with your run-time error at run time. As M.M pointed out, this is only available if your base class has or inherits at least one virtual method.

Now if, by chance, you can be statically certain the dynamic type of your object really is Bar, you can use static_cast. However, if you're mistaken, you have Undefined Behaviour and no opportunity to detect or deal with the error.

eg.

struct Foo { virtual ~Foo(){} };
struct Bar : public Foo {};

// safe, may return nullptr
Bar* safe_ptr_cast(Foo *f) { return dynamic_cast<Bar*>(f); }

// safe, may throw but you can catch it
Bar& safe_ref_cast(Foo &f) { return dynamic_cast<Bar&>(f); }

// unsafe - if you're wrong, you just broke everything
Bar* unsafe_ptr_cast(Foo *f) { return static_cast<Bar*>(f); }

By the way, if your issue with the run time cast is performance, risking UB in order to save notional time before you have code to profile is the definition of premature optimization.

Useless
  • 64,155
  • 6
  • 88
  • 132
  • 1
    Your code is incorrect as `dynamic_cast` may only be used on classes which have at least one virtual function. – M.M Sep 10 '15 at 17:42
  • "the static type of your base class object really is Bar" - that's not what *static type* means. – T.C. Sep 10 '15 at 18:21