С++ не может преобразовать из базы 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
Ответы
Ответ 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 не должен требовать такого рода вещей, но в моем случае я пытался реорганизовать/перепроектировать код, не отказываясь от использования реинтерпрет-кастинга. вообще говоря, вы МОЖЕТЕ избегать такого рода вещей.