Die Bibliothek Boost.Conversion bietet in der Headerdatei boost/cast.hpp
die zwei Cast-Operatoren boost::polymorphic_cast
und boost::polymorphic_downcast
an. Sinn und Zweck dieser Cast-Operatoren ist es, eine dynamische Typumwandlung, die normalerweise mit dynamic_cast
erfolgt, zu präzisieren.
dynamic_cast
struct base1 { virtual ~base1() = default; };
struct base2 { virtual ~base2() = default; };
struct derived : public base1, public base2 {};
void downcast(base1 *b1)
{
derived *d = dynamic_cast<derived*>(b1);
}
void crosscast(base1 *b1)
{
base2 *b2 = dynamic_cast<base2*>(b1);
}
int main()
{
derived *d = new derived;
downcast(d);
base1 *b1 = new derived;
crosscast(b1);
}
Im Beispiel 54.1 wird der Cast-Operator dynamic_cast
zweimal eingesetzt: In der Funktion downcast()
wandelt er den Zeiger auf eine Elternklasse in den Zeiger auf eine Kindklasse um. In der Funktion crosscast()
wandelt er den Zeiger auf eine Elternklasse in einen Zeiger auf eine andere Elternklasse um. Die erste Umwandlung wird als Downcast bezeichnet, die zweite Umwandlung als Crosscast.
Indem die Cast-Operatoren von Boost.Conversion eingesetzt werden, kann wie im Beispiel 54.2 ein Downcast von einem Crosscast im Code unterschieden werden.
polymorphic_downcast
und polymorphic_cast
#include <boost/cast.hpp>
struct base1 { virtual ~base1() = default; };
struct base2 { virtual ~base2() = default; };
struct derived : public base1, public base2 {};
void downcast(base1 *b1)
{
derived *d = boost::polymorphic_downcast<derived*>(b1);
}
void crosscast(base1 *b1)
{
base2 *b2 = boost::polymorphic_cast<base2*>(b1);
}
int main()
{
derived *d = new derived;
downcast(d);
base1 *b1 = new derived;
crosscast(b1);
}
Der Cast-Operator boost::polymorphic_downcast
kann nur für Downcasts verwendet werden. Er verwendet intern static_cast
, um die Umwandlung durchzuführen. Da static_cast
nicht dynamisch überprüft, ob die Typumwandlung gültig ist, darf boost::polymorphic_downcast
nur angewandt werden, wenn sicher ist, dass die Typumwandlung durchgeführt werden darf. Zur Kontrolle greift boost::polymorphic_downcast
in Debug-Versionen auf dynamic_cast
zu und überprüft mit assert()
, ob die Typumwandlung erfolgen darf. Diese Überprüfung findet jedoch nur statt, wenn das Makro NDEBUG
nicht definiert ist, was üblicherweise nur in Debug-Versionen der Fall ist.
Während mit boost::polymorphic_downcast
ein Downcast möglich ist, muss boost::polymorphic_cast
für einen Crosscast verwendet werden. Dieser Cast-Operator verwendet intern dynamic_cast
, da dies der einzige Cast-Operator ist, der tatsächlich einen Crosscast durchführen kann. Der Vorteil von boost::polymorphic_cast
gegenüber dynamic_cast
ist, dass boost::polymorphic_cast
im Fehlerfall eine Ausnahme vom Typ std::bad_cast
wirft. dynamic_cast
hingegen gibt einen Nullzeiger zurück, wenn die Typumwandlung fehlschlägt. Anstatt den Rückgabewert selbst auf einen Nullzeiger zu überprüfen, wird die Überprüfung von boost::polymorphic_cast
abgenommen.
Beide Cast-Operatoren boost::polymorphic_downcast
und boost::polymorphic_cast
können nur verwendet werden, wenn Zeiger umgewandelt werden müssen. Ansonsten muss auf dynamic_cast
zugegriffen werden. So kann boost::polymorphic_downcast
, das auf static_cast
basiert, Objekte vom Typ einer Elternklasse nicht in Objekte vom Typ einer Kindklasse umwandeln. Es ergibt außerdem keinen Sinn, boost::polymorphic_cast
einzusetzen, wenn andere Typen als Zeiger umgewandelt werden sollen, weil dynamic_cast
bei einer derartigen Umwandlung im Fehlerfall selbst eine Ausnahme vom Typ std::bad_cast
wirft.