73

I have four classes:

class A {};

class B : virtual public A {};
class C : virtual public A {};

class D: public B, public C {};

Attempting a static cast from A* to B* I get the below error:

cannot convert from base A to derived type B via virtual base A
m7913d
  • 10,244
  • 7
  • 28
  • 56
Panayiotis Karabassis
  • 2,278
  • 3
  • 25
  • 40

7 Answers7

116

In order to understand the cast system, you need to dive into the object model.

The classic representation of a simple hierarchy model is containment: if B derives from A then the B object will, in fact, contain an A subobject alongside its own attributes.

With this model downcasting is a simple pointer manipulation by an offset known at compilation time, which depends on the memory layout of B.

This is what static_cast does: a static cast is dubbed static because the computation of what is necessary for the cast is done at compile-time, be it pointer arithmetic or conversions (*).

However, when virtual inheritance kicks in, things tend to become a bit more difficult. The main issue is that with virtual inheritance all subclasses share the same instance of the subobject. In order to do that, B will have a pointer to an A, instead of an A proper, and the A base class object will be instantiated outside of B.

Therefore, it's impossible at compilation time to be able to deduce the necessary pointer arithmetic: it depends on the runtime type of the object.

Whenever there is a runtime type dependency, you need RTTI (RunTime Type Information), and making use of RTTI for casts is the job of dynamic_cast.

In summary:

  • compile-time downcast: static_cast
  • run-time downcast: dynamic_cast

The other two are also compile-time casts, but they are so specific that it's easy to remember what they are for... and they are smelly, so better not use them at all anyway.

(*) As noted by @curiousguy in the comments, this only holds for downcasting. A static_cast allows upcasting regardless of virtual or simple inheritance, though then the cast is also unnecessary.

kelalaka
  • 5,064
  • 5
  • 27
  • 44
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 7
    Nice answer which made me understand how virtual inheritance actually works ! +1 – undu Jun 12 '12 at 08:17
  • I like your answer, but the OP was apparently asking about an error for DOWNCASTING rather than upcasting. – h9uest Dec 21 '15 at 23:56
  • @h9uest: Thanks for pointing out the slip-up, I changed "up-casting" to "downcasting" and everything is now well. – Matthieu M. Dec 22 '15 at 07:33
  • False. `static_cast` can be used to convert a derived ptr to a virtual base ptr. – curiousguy Dec 26 '15 at 03:33
  • @curiousguy: Indeed... although you don't even need a cast then, so I didn't think about taking it into account. Which begs the question why downcasting is not available too. – Matthieu M. Dec 26 '15 at 13:54
  • Converting to a base class is possible for non-polymorphic classes. `dynamic_cast` is only possible for polymorphic classes: `dynamic_cast` means "uses the vptr" (in most cases). A conversion to a virtual base class uses either the vptr or an internal pointer (or a fixed offset when the final complete type is known). – curiousguy Dec 26 '15 at 19:39
  • @curiousguy, @matthieu-m: I do not agree. `static_cast` makes the cast at run time. Upcast (without a cast expression) is also done at run time. Although it is true, that the compiled code is shorter than for `dynamic_cast`. For pointers `static_cast` compiles an `if != nullptr`, and then adds an offset **known at compile time**. However, if virtual inheritance is included, `static_cast` and upcast also compiles an `if != nullptr`, and then adds an offset **known at run time**. [Example](https://godbolt.org/z/tSSSyq). `static_cast` can also call ctors., which is also entirely run time. – Dr. Gut Oct 22 '19 at 22:51
  • @Dr.Gut You made many pts here. 1) "_can also call ctors._" Indeed `static_cast` can do many thing. I thought we were discussing ptr casts here, not implicit conv in general or how to construct an obj. 2) Can we agree that the context is: doing the same conversion from type X to type Y either impl., w/ some casting oper or another. 3) Can you describe X and Y such that these choices are valid and **give diff compiled code?** – curiousguy Oct 23 '19 at 00:39
  • @curiousguy: **1)** The (*) sentence ends with "or conversions". So I thought it wants to be general. I edited the answer. It seems clearer now. Hope you will like it. First part is about containment: offset is known at compile-time whether up or downcast. Second part: about virtual inheritance (downcast gives error message, upcast possible, but offset is known at run-time). **2)** Let's focus on casts between related class type pointers. Otherwise the terms compile-time and run-time get easily misinterpreted. **3)** I don't get what you are asking for. What choices? – Dr. Gut Oct 25 '19 at 20:09
  • @Dr.Gut 1) You wanted to be general but seemed to imply that a `static_cast` would always apply a fixed constant offset. 3) The choices of two conversion X and Y, from U->V between a "new style" cast `*_cast`, old style cast, implicit conversion for two given types U,V. In which case are X and Y a) both valid b) different at code gen level? – curiousguy Oct 26 '19 at 04:28
  • @curiousguy: Still not sure what you are asking for. [This](https://godbolt.org/z/FJzBDy) probably is not new to you. So I do not know. Maybe there is no such example. Ask it as a question here on StackOverflow. – Dr. Gut Oct 28 '19 at 19:22
15

As far as I know, you need to use dynamic_cast because the inheritance is virtual and you're downcasting.

Jon Purdy
  • 53,300
  • 8
  • 96
  • 166
7

You can't use static_cast in this situation because the compiler doesn't know the offset of B relative to A at compile time. The offset must be calculated at run-time based on the exact type of the most derived object. Therefore you must use dynamic_cast.

Yakov Galka
  • 70,775
  • 16
  • 139
  • 220
  • When converting a derived to a virtual base, you can use `static_cast`. – curiousguy Dec 26 '15 at 03:36
  • @curiousguy: yes, but the question is about converting base to derived. – Yakov Galka Dec 26 '15 at 19:23
  • It isn't clear why your argument doesn't apply to derived to base also. – curiousguy Dec 26 '15 at 19:41
  • 1
    @curiousguy: well, if you're asking why, then I think it is a good question and I'm not sure what the answer is. I speculate that this is because the derived to base cast is a matter of a simple indirection through the vtable, For the base to derived cast, on the other hand, you must perform the same (non constant time) algorithm as dynamic_cast does for the non-virtual inheritance. Thus there otherwise would not be a difference between static and dynamic casts for virtual inheritance. Or put another way, there is no analogy for non-virtual static_cast in the virtual-inheritance case. – Yakov Galka Dec 26 '15 at 22:41
4

Yes, you have to use a dynamic_cast, but you'll have to make the base class A polymorphic, e.g. by adding a virtual dtor.

Nico
  • 1,554
  • 1
  • 23
  • 35
4

According standard docs,

Section 5.2.9 - 9, for Static Cast,

An rvalue of type “pointer to cv1 B,” where B is a class type, can be converted to an rvalue of type “pointer to cv2 D,” where D is a class derived (clause 10) from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists (4.10), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and B is neither a virtual base class of D nor a base class of a virtual base class of D.

Hence, it is not possible and you should use dynamic_cast...

liaK
  • 11,422
  • 11
  • 48
  • 73
1

I don't know if this is "safe" but.

Assuming

B derived from A (and A pure virtual)

Since I KNOW that a pointer to B still remains a pointer to B.

    class A
    {
            virtual void doSomething(const void* p) const =0;
    };

    class B
    {
    public:
            int value;
            virtual void doSomething(const void*p)const
            {
            const B * other = reinterpret_cast<const B*>(p);
            cout<<"hello!"<< other->value <<endl;
            }
    };

    int main()
    {
            B  foo(1),bar(2);
            A * p = &foo, q=&bar;
            p->doSomething(q);
            return 0;
    }

this program executes and correctly return printing "hello!" and the value of the other object (in this case "2").

by the way, what I'm doing is highly unsafe (personally I give a different ID to every class and I assert after reinterpret casting that current ID is equal to other ID to be sure we are doing something with 2 equal classes) and as you see I limited myself to "const" methods. Thus this will work with "non-const" methods, but if you do something wrong catching the bug will be almost unpossible. And even with assertion there's a 1 chance out of 4 billions to succeed assertion even when it is supposed to fail (assert(ID== other->ID);)

By the way.. A good OO design should not require this kinda of stuff, but in my case I tried to refactor/re-design the code without being able to drop the usage of reinterpret casting. generally speaking you CAN avoid this kind of things.

CoffeDeveloper
  • 7,961
  • 3
  • 35
  • 69
  • Are you sure this is necessary? – Panayiotis Karabassis Nov 16 '12 at 18:35
  • that's specific to your problem. a re-design should prevent this in most cases (avoid my example if you can). oh dear I forget a "const". – CoffeDeveloper Nov 17 '12 at 06:42
  • I mean are you sure you need it? What if I give doSomething an int pointer? That would fail disastrously. Why not use a dynamic cast, and check the result? I don't know what your exact requirements are, but I believe, if you bring in the static polymorphism system (e.g. CRTP pattern) you can come up with something safer. – Panayiotis Karabassis Dec 14 '12 at 13:34
  • static polymorphism is not suitable in lot of cases, anyway in my actual code I'm doing that in a type-safe way, so users have no chance to passa "int*" around. Originally I had a "ID" system, but I found later better system.. still times faster than a dynamic cast. – CoffeDeveloper Jun 02 '13 at 00:39
  • if you are still interested how that code can be usefull you can take a look at a my recent project. void* tricks are used in more than one place.: http://code.google.com/p/infectorpp/ – CoffeDeveloper Jul 13 '13 at 13:21
1

$5.2.9/2- "An expression e can be explicitly converted to a type T using a static_cast of the form static_cast(e) if the declaration “T t(e);” is well-formed, for some invented temporary variable t (8.5)."

In your code you are attempting static_cast with 'T = B*' and 'e = A*'

Now 'B* t(A*)' is not well-formed in C++ (but 'A* t(B*)' is because 'A' is a virtual unambiguous and accessible base of 'B'. Therefore the code gives error.

Chubsdad
  • 24,777
  • 4
  • 73
  • 129