Search code examples
c++multiple-inheritancedynamic-castreinterpret-castclass-template

c++: Is a multiple inheritance from template class and general class valid?


I'm trying to figure out why b->boo() actually calls a.far().
is the multiple inheritance from template class and general class forbidden? why does the inherit order matter? The code is here:

#include <iostream>

template <int somecount>
class inner_parent_class
{
public:
    int array[somecount];
    virtual void far() = 0;
};

class any_class
{
public:
    virtual void boo() = 0;
};

template <int somecount>
class child_class_bad : public inner_parent_class<somecount>, public any_class
{
public:
    virtual void boo() override
    {
        std::cout << "call me" << std::endl;
    }

    virtual void far() override
    {
        std::cout << "do not call me" << std::endl;
    }
};

template <int somecount>
class child_class_good : public any_class, public inner_parent_class<somecount>
{
public:
    virtual void boo() override
    {
        std::cout << "call me" << std::endl;
    }

    virtual void far() override
    {
        std::cout << "do not call me" << std::endl;
    }
};

int main()
{
    {
        child_class_good<32> a;
        any_class* b;
        auto c = dynamic_cast<void*>(&a);
        b = reinterpret_cast<any_class*>(c);
        b->boo();
    }
    {
        child_class_bad<32> a;
        any_class* b;
        auto c = dynamic_cast<void*>(&a);
        b = reinterpret_cast<any_class*>(c);
        b->boo();
    }

    return 0;
}

@ GCC 9.3.0

@ VS 2019 16.5.3

I suppose that both child classes (child_class_good and child_class_bad) are different classes even though their class names are the same, because they are template classes and constructed separately at compiled time. Nevertheless, each class might have its own v-table, so I think calling boo() as their common parent class any_class should correctly work.


Solution

  • reinterpret_cast cannot be used to do what you're trying to do. A reinterpret_cast from a void* to a T* only produces a pointer to a valid T* if the void* pointer it was given was a pointer to an object of type T.

    Doing a dynamic_cast<void*>(p) returns a void* which points to the most-derived object pointed to by p. Since your &a is in fact the most-derived object that it points to, it simply converts the pointer to a void*.

    Then you perform reinterpret_cast<any_class*> on that void*. The void* points to an object of type child_class_good<32> or child_class_bad<32>. Your cast is saying that the pointer actually points to an any_class. This is incorrect (neither type is standard layout, so the layout of the base classes is not defined), and thus attempting to use the results will yield undefined behavior.

    The case that you identify as good is just as invalid as bad; it merely happens to work.

    It is not clear why you're trying to do whatever it is you're trying to do, but there's no valid way to take a void* pointing to the most-derived object of an unknown type and casting it to anything useful. In order to use a void*, you have to know the exact type that was used to produce that void*.