Хм, я думаю, что вижу, в чем проблема:
struct D : B<D>, A { };
Таким образом, вы наследуете оба экземпляра B<D>
и a A
.По сути, это выглядит примерно так:
struct D
{
B<D> implicitly_inherited_B_D;
A implicitly_inherited_A;
};
Теперь вы делаете следующее:
D* d = new D();
void* v = d;
A* a = static_cast<A*>(v);
Проблема в том, что v
теперь указывает на экземпляр D
, который разделяет егоадрес с унаследованным B<D>
экземпляром.Но вы приводите указатель обратно к A*
, однако D
s A
имеет смещение.То, что вы делаете, соответствует:
D* d = new D();
void* v = &d->implicitly_inherited_B_D;
A* a = static_cast<A*>(v);
// or equivalent:
A* aa = reinterpret_cast<A*>(&d->implicitly_inherited_B_D);
Это обязательно приведет к ошибке ...
Если вы хотите привести обратно к A*
, вам нужно убедиться, что ваш указатель действительноуказывает на унаследованный A
в пределах D
- что довольно просто:
D* d = new D();
void* v = static_cast<A*>(d);
// now this will work fine (v points to D's A part):
A* a = static_cast<A*>(v);
D* dd = static_cast<D*>(a); // even this one, original object was constructed as D
Для сравнения:
D* d = new D();
A* a = d;
D* ds = static_cast<D*>(a);
D* dr = reinterpret_cast<D*>(a); // actually undefined behaviour!!!
std::cout << d << std::endl << a << std::endl << ds << std::endl << dr << std::endl;
Предполагая, что адрес d
равен 0x10001000 и A
в пределах D
смещение равно 8 (sizeof(B<D>
+ возможно заполнение байтов для выравнивания), вы увидите такой вывод:
10001000
10001008
10001000
10001008
Обратите внимание, что последняя строка начинается с D*
указатель получен через reinterpret_cast!
Последнее замечание: Помните, что элементы могут быть переставлены - элементы, объявленные первыми предыдущими элементами, объявленными только после, гарантированы для членов в пределах одного и того же класса доступности (публичный / защищенный / частный), между этими разделами компилятору разрешено переупорядочивать.Таким образом, в целом вы можете быть в безопасности, только если вернетесь с void*
так же, как вы привыкли к нему:
void* v = d; // -> need to go back via static_cast<D*>!
A* a = static_cast<A*>(v); // requires v = static_cast<A*>(d);
B<D>* d = static_cast<B<D>*>(v); // requires v = static_cast<B<D>*>(d);
Все остальное - неопределенное поведение (имейте в виду, что ситуация становится еще хуже, как толькопоскольку задействованы виртуальные классы, то, кроме того, существуют указатели vtable ...).