polymorphic_cast Header: "boost/cast.hpp" Polymorphic conversions in C++ are performed via dynamic_cast. A feature of dynamic_cast, which is sometimes also the cause of erroneous code, is that it behaves differently depending on the type with which it is used. dynamic_cast tHRows an exceptionstd::bad_castif the conversion is not possible when used on a reference type. The reason for the exception is simple. There is no such thing as a null reference in C++, so either the conversion succeeds and the result is a valid reference or it fails and you get an exception instead. Of course, when using dynamic_cast to convert a pointer type, failure is indicated by returning the null pointer. dynamic_cast's different behavior depending on whether pointer or reference types are used is a valuable property, because it allows the programmer to express intent. Typically, if a failed conversion doesn't constitute a logical error, the pointer conversion is used, and if it is an error, the reference version is used. Unfortunately, the difference is quite subtleit boils down to an asterisk or an ampersandand it isn't always a natural choice. What if a failed cast to a pointer type is an error? To make that clear by having an exception thrown automatically, and to make the code consistent, Boost offers polymorphic_cast. It always throws a std::bad_cast exception if the conversion fails. In The C++ Programming Language 3 rd Edition, Stroustrup has the following to say about dynamic_cast with pointer types, and the fact that it can return the null pointer: "Explicit tests against 0 can beand therefore occasionally will beaccidentally omitted. If that worries you, you can write a conversion function that throws an exception in case of failure." polymorphic_cast is precisely that conversion function. Usage polymorphic_cast is used just like dynamic_cast, except (pun intended) that it always throws a std::bad_cast on failure to convert. Another feature of polymorphic_cast is that it is a function, and can be overloaded, if necessary. As a natural extension to our C++ vocabulary, it makes code clearer and casts less error prone. To use it, include the header "boost/cast.hpp". The function is parameterized on the type to convert to, and accepts one argument to be converted. template <class Target, class Source> polymorphic_cast(Source* p); It should be mentioned that there is no version of polymorphic_cast for reference types. The reason for this is that the implementation would do exactly what dynamic_cast already does, and there is no need for polymorphic_cast to duplicate existing functionality of the C++ language. The following example shows the syntactic similarity with dynamic_cast. Downcast and Crosscast There are two typical scenarios when using dynamic_cast or polymorphic_cast is appropriate: when downcasting from a base class to a derived class or when crosscasting, which means casting from one base class to another. The following example shows both types of casts using polymorphic_cast. There are two base classes, base1 and base2, and a class derived that inherits publicly from both of the base classes. #include <iostream> #include <string> #include "boost/cast.hpp" class base1 { public: virtual void print() { std::cout << "base1::print()\n"; } virtual ~base1() {} }; class base2 { public: void only_base2() { std::cout << "only_base2()\n"; } virtual ~base2() {} }; class derived : public base1, public base2 { public: void print() { std::cout << "derived::print()\n"; } void only_here() { std::cout << "derived::only_here()\n"; } void only_base2() { std::cout << "Oops, here too!\n"; } }; int main() { base1* p1=new derived; p1->print(); try { derived* pD=boost::polymorphic_cast<derived*>(p1); pD->only_here(); pD->only_base2(); base2* pB=boost::polymorphic_cast<base2*>(p1); pB->only_base2(); } catch(std::bad_cast& e) { std::cout << e.what() << '\n'; } delete p1; } To show how polymorphic_cast works, the first thing we did was to create an instance of derived and manipulate it through various pointers to the base and derived classes. The one function that will work out-of-the-box for p1 is print, which is a virtual function in base1 and derived. We then use a downcast to be able to call only_here, available only in derived: derived* pD=boost::polymorphic_cast<derived*>(p1); pD->only_here(); Note that if the polymorphic_cast fails, a std::bad_cast exception is thrown, so the code is protected by a try/catch block. This behavior is exactly the same as for dynamic_cast using reference types. The pointer pD is then used to call the function only_base2. The function is a non-virtual function in base2, but is also provided by derived, which hides the version in base2. Thus, we need to perform a crosscast to get a pointer to base2 to call base2::only_base2 rather than derived::only_base2. base2* pB=boost::polymorphic_cast<base2*>(p1); pB->only_base2(); Again, if the conversion fails, an exception is thrown. This example shows the ease with which error handling is performed when using polymorphic_cast if failed conversions are considered errors. There is no need to test for null pointers or to propagate an error out of the function explicitly. As we shall see shortly, dynamic_cast sometimes adds unnecessary complexity for this type of code; it may even lead to undefined behavior. dynamic_cast Versus polymorphic_cast To see the difference between these complementary casts, [3] let's put them head to head in a race against complexity. We'll reuse the classes base1, base2, and derived from the previous example. You'll note that the tests for a valid pointer when employing dynamic_cast on pointer types are both tedious and repetitious, which makes the tests unfortunate candidates for being omitted by stressed programmers. [3] Technically, dynamic_cast is a cast operator, whereas polymorphic_cast is a function template. void polymorphic_cast_example(base1* p) { derived* pD=boost::polymorphic_cast<derived*>(p); pD->print(); base2* pB=boost::polymorphic_cast<base2*>(p); pB->only_base2(); } void dynamic_cast_example(base1* p) { derived* pD=dynamic_cast<derived*>(p); if (!pD) throw std::bad_cast(); pD->print(); base2* pB=dynamic_cast<base2*>(p); if (!pB) throw std::bad_cast(); pB->only_base2(); } int main() { base1* p=new derived; try { polymorphic_cast_example(p); dynamic_cast_example(p); } catch(std::bad_cast& e) { std::cout << e.what() << '\n'; } delete p; } The two functions, polymorphic_cast_example and dynamic_cast_example, perform exactly the same work but in different ways. The difference is that wherever a dynamic_cast involving pointers is performed, we must remember to test the returned pointer to see if it is null. In our example, this designates an error, which should result in an exception of type bad_cast being thrown. [4] When using polymorphic_cast, the error handling is localized to the exception handler for std::bad_cast, which means that we do not need to worry about testing any returned values from casting between types. In this trivial example, it's not that hard to remember to test for validity of the returned pointer, but it still requires more work than when using polymorphic_cast. Add a couple of hundred lines of code, and two or three programmers performing maintenance in the function, and the risk of a forgotten test, or failure to throw the appropriate exception, increases drastically. [4] Of course, the returned pointer must always be tested anyway, unless one is absolutely certain that the conversion will not fail. polymorphic_cast Isn't Always the Right Choice If a failed polymorphic pointer cast is not an error, you should use dynamic_cast rather than polymorphic_cast. For example, this is often the case when one uses dynamic_cast to test for certain types. Using exception handling to try conversions to several types makes for inefficient, hard-to-read code. It is this behavior of dynamic_cast that is its real strength. When using both polymorphic_cast and dynamic_cast, you can capture your intent very clearly. Even without polymorphic_cast, if people know about the different ways that dynamic_cast works, it is still possible to achieve the same level of safety, as is shown in the following example.