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

РЕДАКТИРОВАТЬ: Хорошо, как я теперь вижу, это сильно меняет случай, поэтому более точный сценарий таков:

Текущая иерархия в чем-то похожа на эту:

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)


person Adrian Lis    schedule 13.09.2013    source источник
comment
IDerived* d = (IDerived*)d; - опечатка? должен ли второй d быть derived вместо этого?   -  person SingerOfTheFall    schedule 13.09.2013
comment
Спасибо, alredy это заметил и исправил;)   -  person Adrian Lis    schedule 13.09.2013
comment
Кроме того, вам не следует использовать приведение в стиле C в C ++, вместо этого используйте приведение в стиле C ++, например static_cast или dynamic_cast   -  person SingerOfTheFall    schedule 13.09.2013
comment
Послушайте, это совершенно новый вопрос после последнего редактирования! :П   -  person SingerOfTheFall    schedule 13.09.2013
comment
Да, извините за то, что я просто пренебрегла имеющимися здесь шаблонами ролей. Как я уже сказал, я почему-то ненавижу все эти причудливые сложные решения, в данном случае я не вижу лучшего варианта.   -  person Adrian Lis    schedule 13.09.2013
comment
Попробуйте выполнить приведение к Derived* вместо IDerived*. IDerived не является потомком IBase, а Derived является потомком.   -  person SingerOfTheFall    schedule 13.09.2013
comment
@JurajBlaho он не может использовать dynamic_cast, поэтому виртуальное наследование не помогает.   -  person Simple    schedule 13.09.2013
comment
@SingerOfTheFall, в стране на расстоянии 1500 строк одна функция получает указатель на IBase * функция уверена, что тип является одним из шаблонных производных, но теперь не имеет параметров шаблона;) поэтому я не могу привести к точному типу, только к интерфейсу IDerived. Но, похоже, я тоже не могу;)   -  person Adrian Lis    schedule 13.09.2013
comment
Частично актуально: stackoverflow.com/questions/142644/   -  person SingerOfTheFall    schedule 13.09.2013
comment
@JurajBlaho Как вы предлагаете, чтобы он проводил кастинг с IBase* на Derived* без dynamic_cast, когда IBase - виртуальная база? Вы не можете использовать static_cast, потому что смещения неизвестны во время компиляции.   -  person Simple    schedule 13.09.2013
comment
@ Просто, ну, всегда есть reinterpret_cast   -  person SingerOfTheFall    schedule 13.09.2013
comment
@SingerOfTheFall reinterpret_cast небезопасен, потому что Derived* будет указывать на неправильный адрес.   -  person Simple    schedule 13.09.2013
comment
@ Простое, приведение в стиле c не намного безопаснее, правда ...   -  person SingerOfTheFall    schedule 13.09.2013
comment
@SingerOfTheFall Верно, я сказал ему не использовать приведение в стиле C. В этом случае приведение стиля C эквивалентно reinterpret_cast, поэтому оба они плохие.   -  person Simple    schedule 13.09.2013
comment
позвольте нам продолжить это обсуждение в чате   -  person SingerOfTheFall    schedule 13.09.2013


Ответы (1)


Вы выполняете перекрестный бросок; 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() {}};
person Simple    schedule 13.09.2013
comment
Спасибо за ответ, но это не так просто, и я должен уточнить с самого начала;) Пожалуйста, посмотрите правку - person Adrian Lis; 13.09.2013
comment
@AdrianLis Если вы не можете использовать dynamic_cast, тогда нет безопасного способа перейти от IBase к IDerived в целом. - person Simple; 13.09.2013
comment
Спасибо за совет, я изучу предложение EDIT2 и посмотрю, решило ли оно проблему в более крупномасштабном сценарии;) Я дам вам знать - person Adrian Lis; 13.09.2013
comment
При очень кратком тестировании кажется, что промежуточный класс решил проблему. Еще не уверен, как он будет сочетаться с остальной частью шаблона, но наверняка решил описанную проблему, поэтому я помечаю ваш ответ как решение. Спасибо ;) - person Adrian Lis; 13.09.2013