Приведение производного класса к одной из баз через базовый указатель


EDIT: хорошо, как я вижу сейчас, это сильно меняет дело, поэтому более точный сценарий таков:

Иерархия, которую я в настоящее время имею, несколько похожа на эту:

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 через derived работают нормально, но когда я пытаюсь привести derived к IDerived, я получаю ошибку вызова любой функции:

IDerived* d = (IDerived*)derived;
d->Bar(); <- boom error ;) 

Я предполагаю, что такой бросок незаконен, но как я могу приведите указатель так, чтобы я мог получить доступ к методам интерфейса IDerived (предпочтительно без dynamic_cast, я бы предпочел хороший и портативный хак, если он существует ;))? Можно ли как-то вычислить смещение к указателю так, чтобы использовалась правильная vtable и все работало так, как должно ?

Я программирую довольно долго, но я всегда уклонялся от всех этих причудливых шуток над инженерными системами с тоннами интерфейсов и шаблонов, и теперь я обречен сделать один себя.

EDIT: теперь, как вы можете видеть, это становится сложным и трудным. Я не знаю точный тип производного, который я получаю, поскольку он шаблонен, также функция CreateDerived шаблонна и возвращает интерфейс.

Кроме того, одно из требований-не использовать dynamic_cast (RTTI отключен в проекте)

1 3

1 ответ:

Вы выполняете перекрестный бросок; IBase и IDerived не связаны. Вам нужно сначала привести к Derived, а затем к IDerived. Если бы вы использовали static_cast, а не c-приведение, компилятор поймал бы это для вас во время компиляции.

Я предполагаю, что вы знаете, что IBase на самом деле является Derived, потому что вы использовали c-приведение, но если вы этого не сделаете, то вы также можете использовать dynamic_cast для безопасного выполнения перекрестных приведений.

EDIT: если вы не можете использовать RTTI и не знаете динамический тип объекта, то виртуальный наследование и dynamic_cast уходят в окно. Когда вы вызываете CreateDerived, похоже, что вы знаете динамический тип объекта (из-за его аргументов шаблона), поэтому у вас может быть std::map<IBase*, IDerived*>, а затем после вызова CreateDerived<TT, TB, TI...>() вы можете static_cast IBase* to Derived<TT, TB, TI...>*, а затем вставить указатель в качестве ключа и значения в карту.

Просто включите RTTI; это становится сложным. >.

EDIT 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() {}};