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

后端 未结 7 1466
不知归路
不知归路 2020-12-04 16:43

I have three classes:

class A {};

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

class D: public B, public C {};

Attemptin

相关标签:
7条回答
  • 2020-12-04 16:51

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

    0 讨论(0)
  • 2020-12-04 17:04

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

    0 讨论(0)
  • 2020-12-04 17:04

    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 讨论(0)
  • 2020-12-04 17:07

    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.

    0 讨论(0)
  • 2020-12-04 17:08

    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.

    0 讨论(0)
  • 2020-12-04 17:10

    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.

    0 讨论(0)
提交回复
热议问题