Существует огромная разница между static_cast
и dynamic_cast
, я просто сведу обсуждение к миру объектов.
A static_cast
может быть использовано для (печально известного) приведения,То есть:
void foo(Base& b) { Derived& d = static_cast<Derived&>(b); }
Компилятор может оценить, является ли это законным или нет, поскольку, имея определение Derived
, он знает , является ли Derived
на самом деле потомкомBase
.
Для нетривиальных иерархий:
struct Base {};
struct D1: Base {};
struct D2: Base {};
struct Derived: D1, D2 {};
Это приведет к ошибке: компилятор не будет знать, с какой из баз (одна из D1
илиодин из D2
, из которого вы пришли).
Или, если вы использовали virtual
наследование:
struct VDerived: virtual VBase {};
, компилятору потребуется информация времени выполнения, и, следовательно, компиляция тоже не удалась.
A dynamic_cast
однако гораздо умнее, хотя это происходит за счет накладных расходов времени выполнения.Компилятор генерирует информацию об объектах, обычно известных как RTTI (информация о типе RunTime), которую dynamic_cast
будет исследовать, чтобы разрешить:
Cast в зависимости от истинного типа времени выполнения:
// will throw `bad_cast` if b is not a `Derived`
void foo(Base& b) { Derived& d = dynamic_cast<Derived&>(b); }
Приведение через ветви:
struct X {};
struct Y {};
void foo(X* x) { Y* y = dynamic_cast<Y*>(x); }
// If I add:
struct Z: X, Y {};
// then if x is in fact a Z, y will be non-null
Приведение, когда задействовано виртуальное наследование.
void bar(VBase* vb) { VDerived* vd = dynamic_cast<VDerived*>(vb); }
Приведение к void*
для получения истинного адреса объекта (это несколько особенное).
void foobar(X* x)
{
void* xAddr = dynamic_cast<void*>(x);
Y* y = dynamic_cast<Y*>(y);
void* yAddr = dynamic_cast<void*>(y);
Z* z = dynamic_cast<Z*>(x);
void* zAddr = dynamic_cast<void*>(z);
if (z) { assert(xAddr == yAddr && xAddr == zAddr); }
}
Обратите внимание, что это по-прежнему не работает, если существует неоднозначность из-за иерархии (как в примере D1
/ D2
выше).