Can't Downcast Because Class Is Not Polymorphic

Can't downcast because class is not polymorphic?

Syntax errors non-withstanding, you cannot dynamic_cast a non-polymorphic type. static_cast is the cast you would use in this case, if you know that it is in fact an object of the target type.

The reason why: static_cast basically has the compiler perform a check at compile time "Could the input be cast to the output?" This is can be used for cases where you are casting up or down an inheritance hierarchy of pointers (or references). But the check is only at compile time, and the compiler assumes you know what you are doing.

dynamic_cast can only be used in the case of a pointer or reference cast, and in addition to the compile time check, it does an additional run time check that the cast is legal. It requires that the class in question have at least 1 virtual method, which allows the compiler (if it supports RTTI) to perform this additional check. However, if the type in question does not have any virtual methods, then it cannot be used.

The simplest case, and probably worthwhile if you're passing pointers around like this, is to consider making the base class's destructor virtual. In addition to allowing you to use dynamic cast, it also allows the proper destructors to be called when a base class pointer is deleted.

How to downcast from non-polymorphic virtual base class?

There is an implicit unambigious conversion from MostDerived& to its ViBase&. A static_cast can express such a conversion explicitly, and can also do the opposite conversion. That’s the kinds of conversions that static_cast does.

As the OP noted a static_cast down from virtual base is invalid.

The source code below illustrates why:

#include <iostream>
using namespace std;

struct B { virtual ~B(){} };
struct D: virtual B {};
struct E: virtual B {};
struct X: D, E {};

auto main() -> int
{
X x;
B& b = static_cast<E&>( x );

// Can't do the following for the address adjustment that would work for
// D sub-object won't work for E sub-object, yet declarations of D and E
// are identical -- so the address adjustment can't be inferred from that.
//
//static_cast<D&>( b );

// This is OK:
dynamic_cast<D&>( b );
}

Essentially, as this shows, you can't infer the address adjustment from the declaration of D (or E) alone. And neither can the compiler. This also rules out reinterpret_cast.

Getting source type is not polymorphic when trying to use dynamic_cast

You need to make A polymorphic, which you can do by adding a virtual destructor or any virtual function:

struct A {
virtual ~A() = default;
};

or, before C++11,

struct A {
virtual ~A() {}
};

Note that a polymorphic type should have a virtual destructor anyway, if you intend to safely call delete on instances of a derived type via a pointer to the base.

Does downcasting defeat the purpose of polymorphism?

Now if there are more than one type of cars which is retractable, say such cars are CarA, CarB, and CarC (in addition to Lamborghini), then are you going to write this:

if(Lamborghini* lambo = dynamic_cast<Lamborghini*>(cars[i])) {
lambo->retractTheRoof();
}
else if(CarA * pCarA = dynamic_cast<CarA*>(cars[i])) {
pCarA->retractTheRoof();
}
else if(CarB * pCarB = dynamic_cast<CarB*>(cars[i])) {
pCarB->retractTheRoof();
}
else if(CarC * pCarC = dynamic_cast<CarC*>(cars[i])) {
pCarC->retractTheRoof();
}

So a better design in such cases would be this: add an interface called IRetractable and derive from it as well:

struct IRetractable 
{
virtual void retractTheRoof() = 0;
};

class Lamborghini : public Car, public IRetractable {
//...
};

class CarA : public Car, public IRetractable {
//...
};
class CarB : public Car, public IRetractable {
//...
};
class CarC : public Car, public IRetractable {
//...
};

Then you can simply write this:

if(IRetractable *retractable =  dynamic_cast<IRetractable *>(cars[i])) 
{
retractable->retractTheRoof(); //Call polymorphically!
}

Cool? Isn't it?

Online demo : http://www.ideone.com/1vVId

Of course, this still uses dynamic_cast, but the important point here is that you're playing with interfaces only, no need to mention concrete class anywhere. In other words, the design still makes use of runtime-polymorphism as much as possible. This is one of the principle of Design Patterns:

"Program to an 'interface', not an 'implementation'." (Gang of Four 1995:18)

Also, see this:

  • What does it mean to "program to an interface"?

Other important point is that you must make the destructor of Car (base class) virtual:

class Car{
public:
virtual ~Car() {} //important : virtual destructor
virtual int goFast() = 0;
};

Its imporant because you're maintaining a vector of Car*, that means, later on you would like to delete the instances through the base class pointer, for which you need to make ~Car() a virtual destructor, otherwise delete car[i] would invoke undefined behaviour.

C++ dynamic_cast - polymorphic requirement and downcasting

From 5.2.7/1 [expr.dynamic.cast] :

The result of the expression dynamic_cast<T>(v) is the result of converting the expression v to type
T.

[...]

If T is "pointer to cv1 B" and v has type "pointer to cv2 D" such that B is a base class of D, the result is a
pointer to the unique B sub-object of the D object pointed to by v.

[...]

Otherwise, v shall be a pointer to or an lvalue of a polymorphic type.

The standard even provides the following example which illustrates that the polymorphic type requirement does not stand for derived to base conversion :

struct B {};
struct D : B {};
void foo(D* dp)
{
B* bp = dynamic_cast<B*>(dp); // equivalent to B* bp = dp;
}

dynamic cast from father to child

dynamic_cast generally requires polymorphic types. That means the types involved must have at least one virtual member function (can be a destructor).

static_cast on derived classes when base turns from not polymorphic to polymorphic

Background you didn't include - boost has polymorphic_cast as a wrapper around dynamic_cast<> that throws when the cast fails. static_cast<> is fine if you're certain that the data is of the type you're casting to... there is no problem with or without virtual members, and the code you include saying it won't compile will compile and run just fine as is.

I guess you're thinking about the possibility to accidentally cast to another derived class? That's the utility/danger of casting, isn't it? You can add a virtual destructor then use dynamic_cast<>, as strictly speaking RTTI is only available for types with one or more virtual functions.

Code written with static_cast<> will continue to handle the same type safely irrespective of the introduction of virtual functions... it's just that if you start passing that code other types (i.e. not CDerived or anything publicly derived therefrom) then you will need the dynamic_cast<> or some other change to prevent incompatible operations.



Related Topics



Leave a reply



Submit