Подтвердить что ты не робот

С++ не может преобразовать из базы A в производный тип B через виртуальную базу A

У меня есть три класса:

class A {};

class B : virtual public A {};
class C : virtual public A {};

class D: public B, public C {};

Попытка статического акта с A * на B * Я получаю следующую ошибку:

cannot convert from base A to derived type B via virtual base A
4b9b3361

Ответ 1

Чтобы понять систему литья, вам нужно погрузиться в объектную модель.

Классическое представление простой иерархической модели - это сдерживание: если B происходит от A, то объект B фактически содержит подобъект A вместе со своими собственными атрибутами.

С помощью этой модели downcasting - простая манипуляция указателем со смещением, известным во время компиляции, которое зависит от расположения памяти B.

Это то, что делает static_cast: статический актер дублируется как статический, потому что вычисление того, что необходимо для создания, выполняется во время компиляции, будь то арифметика указателей или преобразований (*).

Однако, когда virtual наследование ударов в вещах, как правило, становится немного сложнее. Основная проблема заключается в том, что при наследовании virtual все подклассы имеют один и тот же экземпляр подобъекта. Для этого B будет иметь указатель на A вместо правильного A, а объект базового класса A будет создан вне B.

Следовательно, во время компиляции невозможно вывести необходимую арифметику указателя: она зависит от типа среды выполнения объекта.

Всякий раз, когда существует зависимость типа времени выполнения, вам нужна RTTI (информация типа RunTime), а использование RTTI для трансляций - это задание dynamic_cast.

Вкратце:

  • скомпилировать время: static_cast
  • время выполнения: dynamic_cast

Остальные два - это также компиляции, но они настолько специфичны, что легко запомнить, для чего они нужны... и они вонючие, поэтому лучше вообще не использовать их вообще.

(*) Как отмечалось в комментариях @curiousguy в комментариях, это справедливо только для downcasting. A static_cast позволяет повышать уровень, независимо от виртуального или простого наследования, хотя и приведение в действие также не нужно.

Ответ 2

Насколько мне известно, вам нужно использовать dynamic_cast, потому что наследование virtual, и вы угасаете.

Ответ 3

Вы не можете использовать static_cast в этой ситуации, потому что компилятор не знает смещения B относительно A во время компиляции. Смещение должно рассчитываться во время выполнения на основе точного типа самого производного объекта. Поэтому вы должны использовать dynamic_cast.

Ответ 4

Да, вам нужно использовать dynamic_cast, но вам придется сделать базовый класс A полиморфным, например. добавив виртуальный dtor.

Ответ 5

В соответствии со стандартными документами

Раздел 5.2.9 - 9, для Static Cast,

Значение типа "указатель на cv1 B", где B является типом класса, может быть преобразовано в rvalue типа "указатель на cv2 D", где D - это производный класс (раздел 10) из B, если действительное стандартное преобразование из "указателя на D" в "указатель на B" существует (4.10), cv2 является той же самой cv-квалификацией, что и более высокая cv-квалификация, чем cv1 и B не является ни виртуальным базовым классом D и базовый класс виртуального базового класса D.

Следовательно, это невозможно, и вы должны использовать dynamic_cast...

Ответ 6

$5.2.9/2- "Выражение e может быть явно преобразован в тип T, используя static_cast формы static_cast (e) если декларация" T t (e) "; хорошо сформирована, для некоторых придумал временную переменную t (8.5)."

В вашем коде вы пытаетесь static_cast с 'T = B *' и 'e = A *'

Теперь "B * t (A *)" не корректно сформирован в С++ (но "A * t (B *)" - это потому, что "A" является виртуальной однозначной и доступной базой "B". код дает ошибку.

Ответ 7

Я не знаю, является ли это "безопасным", но.

Полагая

B, полученный из A (и чистого виртуального)

Так как я ЗНАЮ, что указатель на B по-прежнему остается указателем на B.

    class A
    {
            virtual void doSomething(const void* p) const =0;
    };

    class B
    {
    public:
            int value;
            virtual void doSomething(const void*p)const
            {
            const B * other = reinterpret_cast<const B*>(p);
            cout<<"hello!"<< other->value <<endl;
            }
    };

    int main()
    {
            B  foo(1),bar(2);
            A * p = &foo, q=&bar;
            p->doSomething(q);
            return 0;
    }

эта программа выполняет и правильно возвращает печать "привет!". и значение другого объекта (в данном случае "2" ).

Кстати, то, что я делаю, очень небезопасно (лично я даю другой идентификатор каждому классу и утверждаю, что после повторного интерпретации, что текущий идентификатор равен другому идентификатору, мы уверены, что делаем что-то с 2 равными классами), и, как вы видите, я ограничился методами "const". Таким образом, это будет работать с "неконстантными" методами, но если вы сделаете что-то неправильно, то поймать ошибку почти невозможно. И даже с утверждением есть 1 шанс из 4 миллиардов, чтобы добиться успеха, даже когда он должен потерпеть неудачу (assert (ID == other- > ID);)

Кстати, хороший дизайн OO не должен требовать такого рода вещей, но в моем случае я пытался реорганизовать/перепроектировать код, не отказываясь от использования реинтерпрет-кастинга. вообще говоря, вы МОЖЕТЕ избегать такого рода вещей.