РЕДАКТИРОВАТЬ: Хорошо, как я вижу сейчас, это сильно меняет дело, поэтому более точный сценарий таков:
Иерархия, которая у меня сейчас есть, похожа на эту:
class IBase() { virtual void Foo() = 0; };
class Base() : public IBase { virtual void Foo() { } };
class IDerived() { virtual void Bar() = 0; };
template<typename TT, typename TB, typename... TI>
class Derived : public Base, public IDerived { virtual void Bar() {}};
template<typename TT, typename TB, typename... TI>
IBase* CreateDerived() { return new Derived(); }
IBase* derived = CreateDerived<some types...>();
Я получаю сообщение об ошибке с Visual Studio при попытке привести объект и вызвать функцию:
Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call.
Все вызовы интерфейса IBase через производные работают нормально, но когда я пытаюсь привести производные к IDerived, я получаю сообщение об ошибке при вызове любой функции:
IDerived* d = (IDerived*)derived;
d->Bar(); <- boom error ;)
Я предполагаю, что такое приведение является недопустимым, но как я могу привести указатель, чтобы я мог получить доступ к методам интерфейса IDerived (предпочтительно без dynamic_cast, я бы предпочел хороший и переносимый хак, если таковой существует;))? Можно ли как-то рассчитать смещение указателя, чтобы использовать правильный vtable и все работает как надо?
Я программирую довольно долгое время, но я всегда уклонялся от всей этой причудливой манеры над созданными системами с тоннами интерфейсов и шаблонов, и теперь я обречен делать это сам.
РЕДАКТИРОВАТЬ: Теперь, как вы можете видеть, это становится сложно и сложно. Я не знаю точный тип производного, который я получаю, поскольку он шаблонизирован, также функция CreateDerived является шаблонной и возвращает интерфейс.
Также одно из требований — не использовать dynamic_cast (RTTI отключен в проекте).
Вы выполняете перекрестный бросок; IBase
а также IDerived
не связаны. Вы должны бросить на Derived
сначала, а потом IDerived
, Если вы использовали static_cast
а не C-бросок, компилятор поймал бы это для вас во время компиляции.
Я полагаю, вы знаете, что IBase
действительно Derived
потому что вы использовали C бросок, но если вы этого не сделаете, вы также можете использовать dynamic_cast
безопасно выполнять перекрестные броски.
РЕДАКТИРОВАТЬ: Если вы не можете использовать RTTI и вы не знаете динамический тип объекта, то виртуальное наследование и dynamic_cast
выйти из окна. Когда вы звоните CreateDerived
похоже, вы знаете динамический тип объекта (из-за его аргументов шаблона), так что вы можете иметь std::map<IBase*, IDerived*>
а затем после CreateDerived<TT, TB, TI...>()
позвонить можно static_cast
IBase*
в Derived<TT, TB, TI...>*
а затем вставьте указатель как ключ и значение в карту.
Просто включите RTTI; это становится сложным. >.<
РЕДАКТИРОВАТЬ 2: В качестве альтернативы, вы, кажется, знаете, что объект, на который указывает IBase*
также вытекает из IDerived*
, Если вы можете изменить иерархию классов, у вас может быть абстрактный базовый класс, производный от обоих IBase
а также IDerived
а затем есть Derived
происходят из этого нового базового класса. Тогда ты можешь static_cast
из IBase*
в новый класс в иерархии, а затем IDerived*
,
Это будет выглядеть примерно так:
class IBase { virtual void Foo() = 0; };
class Base : public IBase { virtual void Foo() { } };
class IDerived { virtual void Bar() = 0; };
class BaseAndDerived : public Base, public IDerived { };
template<typename TT, typename TB, typename... TI>
class Derived : public BaseAndDerived { virtual void Bar() {}};
Других решений пока нет …