0

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.

curiousguy
  • 8,038
  • 2
  • 40
  • 58
Babol
  • 3
  • 2
  • 3
    `child_class_good` and `child_class_bad` are not classes; they are class templates. They do in fact have different names—I'm not sure what "their class names are the same" is supposed to mean. `child_class_good<32>` and `child_class_bad<32>` *are* classes, and you can clearly see they are different with different names. The `dynamic_cast`s do nothing: both of the `&a`s already point to most derived objects and there's nothing to do. I'm fairly sure both `reinterpret_cast`s are undefined behavior since neither class is standard-layout and you're not casting to the exact right type. – HTNW May 04 '20 at 02:11

1 Answers1

3

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

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982