C ++ не может преобразовать из базы 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

Ответы на вопрос(7)

Решение Вопроса

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

Классическим представлением простой модели иерархии является сдерживание: что еслиB происходит отA тогдаB объект на самом деле будет содержатьA подобъект наряду со своими собственными атрибутами.

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

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

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

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

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

В итоге:

время компиляции:static_castвремя выполнения downcast:dynamic_cast

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

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

 h9uest22 дек. 2015 г., 00:56
Мне нравится ваш ответ, но ОП, по-видимому, спрашивал об ошибке для DOWNCASTING, а не для обновления.
 curiousguy26 дек. 2015 г., 04:33
Ложь.static_cast может использоваться для преобразования производного ptr в виртуальный базовый ptr.
 undu12 июн. 2012 г., 10:17
Хороший ответ, который заставил меня понять, как на самом деле работает виртуальное наследование! +1
 Matthieu M.22 дек. 2015 г., 08:33
@ h9uest: Спасибо, что указали на ошибку, я поменял «повышающий» на «понижающий», и теперь все хорошо.
 curiousguy26 дек. 2015 г., 20:39
Преобразование в базовый класс возможно для неполиморфных классов.dynamic_cast возможно только для полиморфных классов:dynamic_cast означает «использует vptr» (в большинстве случаев). Преобразование в виртуальный базовый класс использует либо vptr, либо внутренний указатель (или фиксированное смещение, когда известен окончательный тип завершения).
 Matthieu M.26 дек. 2015 г., 14:54
@curiousguy: Действительно ... хотя тогда вам даже не нужен актерский состав, поэтому я не думал о его принятии во внимание. В связи с этим возникает вопрос, почему уныние также недоступно.

Если предположить,

B получен из A (и 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»).

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

Кстати ... Хороший ОО-дизайн не должен требовать такого рода вещей, но в моем случае я попытался реорганизовать / перепроектировать код, не имея возможности отказаться от использования переинтерпретации приведения. Вообще говоря, вы можете избежать такого рода вещей.

 Panayiotis Karabassis14 дек. 2012 г., 14:34
Я имею в виду, вы уверены, что вам это нужно? Что если я дам doSomething указатель на int? Это потерпит катастрофический провал. Почему бы не использовать динамическое приведение и проверить результат? Я не знаю, каковы ваши точные требования, но я верю, что если вы внедрите систему статического полиморфизма (например, шаблон CRTP), вы сможете придумать что-то более безопасное.
 Panayiotis Karabassis16 нояб. 2012 г., 19:35
Вы уверены, что это необходимо?
 GameDeveloper02 июн. 2013 г., 02:39
статический полиморфизм не подходит во многих случаях, во всяком случае, в моем собственном коде я делаю это безопасным для типов способом, поэтому у пользователей нет шансов передать «int *». Первоначально у меня была система «ID», но позже я обнаружил, что система получше ... все же в разы быстрее, чем при динамическом приведении.
 GameDeveloper17 нояб. 2012 г., 07:42
это специфично для вашей проблемы. Редизайн должен предотвратить это в большинстве случаев (избегайте моего примера, если можете). о дорогой я забыл "const".
 GameDeveloper13 июл. 2013 г., 15:21
если вам все еще интересно, как этот код может быть полезен, вы можете взглянуть на мой недавний проект. трюки void * используются более чем в одном месте .:code.google.com/p/infectorpp

Раздел5.2.9 - 9, заСтатический бросок,

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

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

dynamic_cast потому что наследствоvirtual и ты удручен.

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

 curiousguy26 дек. 2015 г., 20:41
Непонятно, почему ваш аргумент не относится и к производной базе.
 ybungalobill26 дек. 2015 г., 20:23
@curiousguy: да, но вопрос о преобразовании базы в производную.
 curiousguy26 дек. 2015 г., 04:36
При преобразовании производной в виртуальную базу вы можете использоватьstatic_cast.
 ybungalobill26 дек. 2015 г., 23:41
@curiousguy: ну, если вы спрашиваете, почему, то я думаю, что это хороший вопрос, и я не уверен, каков ответ. Я предполагаю, что это происходит потому, что преобразование производного в базовое является простым перенаправлением через виртуальную таблицу. С другой стороны, для преобразования базового в производное необходимо выполнить тот же алгоритм (не с постоянным временем), что и для dynamic_cast для не виртуальное наследование. Таким образом, в противном случае не было бы разницы между статическим и динамическим приведением для виртуального наследования. Или, другими словами, в случае виртуального наследования нет аналогии для не виртуального static_cast.

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

 Liton19 сент. 2010 г., 22:13
или добавление хотя бы одного виртуального метода.

$ 5..9 / - «Выражение e может быть явно преобразовано в тип T с использованием static_cast вида static_cast (e), если объявление« T t (e); »правильно сформировано для некоторой изобретенной временной переменной t (8.5) «.

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

Теперь «B * t (A *)» не является правильно сформированным в C ++ (но «A * t (B *)» объясняется тем, что «A» является виртуальной однозначной и доступной базой «B». Поэтому код выдает ошибку ,

 curiousguy26 дек. 2015 г., 04:35
Неправильная цитата.

Ваш ответ на вопрос