开发者

C++ cannot convert from base A to derived type B via virtual base A

开发者 https://www.devze.com 2023-01-16 23:05 出处:网络
I have four classes: class A {}; class B : virtual public A {}; class C : vi开发者_如何学JAVArtual public A {};

I have four classes:

class A {};

class B : virtual public A {};
class C : vi开发者_如何学JAVArtual 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


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.


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


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.


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.


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


$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.


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消