0

I have two base classes (base_1, base_2). They both have one pure virtual method. I have a class (class_1), which inherits them both and implements their pure virtuals. When I'm calling them from it, it's okay. When I am casting class_1 to void* and then to base_2*, it's not okay - it calls wrong method. I know, that the problem is related to multiple inheritance and offsets. But I don't know what is it, what is hapenning and why is it happening. I also would like to know, how can I achieve correct casting. Here is the code, which reproduces the problem.

class base_1
{
public:
    virtual int doo_base_1() = 0;
};

class base_2
{
public:
    virtual int doo_base_2() = 0;
};

class class_1 : public base_1, public base_2
{
public:
    virtual int doo_base_1() override
    {
        return 1;
    }
    virtual int doo_base_2() override
    {
        return 2;
    }
};

inline void doo()
{
    class_1* ptr = new class_1;
    void* void_ptr = ptr;
    auto result_1 = ptr->doo_base_2(); // Correct.
    auto result_2 = ((base_2*)void_ptr)->doo_base_2(); // Incorrect - it calls wrong method (in my case - doo_base_1, so the result is 1).
}
  • See the linked answer for `dynamic_cast`. – Botje Jun 14 '22 at 07:52
  • Thank you, I have read the most popular answer about dynamic_cast, but I still don't understand how can it solve my problem. I can't use dynamic_cast on void*, but I'd like to convert void to base_2 without knowing, is it class_1 or not. – Stanislav Volkovich Jun 14 '22 at 08:05
  • You cannot do that. casting to `void*` destroys information. – Botje Jun 14 '22 at 08:09
  • Thank you again. Now I know it's not possible. – Stanislav Volkovich Jun 14 '22 at 08:11
  • 1
    Well.... it _is_ possible, BUT extremely dirty (also undefined behavior) and not recommended at all except when you have absolutely zero alternatives and no fear. I've had to do it before when interacting with a private API that I needed to make it do things it wasn't intended to do. The idea is to build a mapping between known possible addresses and use the virtual table (compiler-dependent, but typically found at the object's base address). Your mileage WILL vary. Use at your own risk. https://godbolt.org/z/TdnjTW5xG – paddy Jun 14 '22 at 10:31
  • 1
    `void_ptr` was set from a `class_1*`. The only legitimate thing you can do with `void_ptr` is `static_cast(void_ptr)`. This code does `(base_2*)void_ptr`, but `void_ptr` is not a `base_2*`. You've lied to the compiler, and you've done it with a big hammer cast. Hijinks and hilarity ensue. – Eljay Jun 14 '22 at 11:07
  • Once you throw away the type information (with a cast to `void*`) there's no way to get it back. So don't throw it away. – Pete Becker Jun 14 '22 at 15:52

0 Answers0