C++ cannot convert from base A to derived type B via virtual base A
Solution 1
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.
Solution 2
As far as I know, you need to use dynamic_cast
because the inheritance is virtual
and you're downcasting.
Solution 3
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
.
Solution 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.
Solution 5
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
...
Panayiotis Karabassis
Updated on October 23, 2020Comments
-
Panayiotis Karabassis over 3 years
I have three 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
-
Liton over 13 yearsor adding at least one virtual method.
-
undu almost 12 yearsNice answer which made me understand how virtual inheritance actually works ! +1
-
Panayiotis Karabassis over 11 yearsAre you sure this is necessary?
-
CoffeDeveloper over 11 yearsthat'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".
-
Panayiotis Karabassis over 11 yearsI 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.
-
CoffeDeveloper almost 11 yearsstatic 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 almost 11 yearsif 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.: code.google.com/p/infectorpp
-
h9uest over 8 yearsI like your answer, but the OP was apparently asking about an error for DOWNCASTING rather than upcasting.
-
Matthieu M. over 8 years@h9uest: Thanks for pointing out the slip-up, I changed "up-casting" to "downcasting" and everything is now well.
-
curiousguy over 8 yearsFalse.
static_cast
can be used to convert a derived ptr to a virtual base ptr. -
curiousguy over 8 yearsWhen converting a derived to a virtual base, you can use
static_cast
. -
Matthieu M. over 8 years@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.
-
Yakov Galka over 8 years@curiousguy: yes, but the question is about converting base to derived.
-
curiousguy over 8 yearsConverting 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 over 8 yearsIt isn't clear why your argument doesn't apply to derived to base also.
-
Yakov Galka over 8 years@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.
-
Dr. Gut over 4 years@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 fordynamic_cast
. For pointersstatic_cast
compiles anif != nullptr
, and then adds an offset known at compile time. However, if virtual inheritance is included,static_cast
and upcast also compiles anif != nullptr
, and then adds an offset known at run time. Example.static_cast
can also call ctors., which is also entirely run time. -
curiousguy over 4 years@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? -
Dr. Gut over 4 years@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?
-
curiousguy over 4 years@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? -
Dr. Gut over 4 years@curiousguy: Still not sure what you are asking for. This 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.