In the general case, it is (a very well deserved) Undefined Behavior to dowcast from a (dynamic) Base
to one of the deriving classes Derived
class Base
{
public:
virtual void foo()
{ /* does something */ }
int a;
}
class Derived : public Base
{
public:
virtual void foo()
{ /* does something different */ }
double b;
}
Base obj;
Derived derObj = *static_cast<Derived *>(&obj); // <- here come the demons
In the current implementation approach of compilers, here there would obviously be at least the problems of inconsistent values in the Vtable and b containing garbage values. So it makes sense the standard does not define the behavior of a downcast in those conditions.
Yet I was curious to know if there were some concessions to this rule in specific cases ? For an example :
class Base
{
public:
void foo()
{ /* does something */ }
int a = 1;
double b = 2.;
}
class DerivedForInt : public Base
{
int getVal()
{ return a }
}
Base obj;
DerivedForInt derObj = *static_cast<DerivedForInt *>(&obj); // <- still an UB ?
Here we can easily imagine compiler doing the right thing. But from the standard perspective, is it still undefined ?
Edit : static_cast is a random choice for illustration purpose, it is also interesting if working with other casts !
Downcasting is not allowed without an explicit type cast. The reason for this restriction is that the is-a relationship is not, in most of the cases, symmetric. A derived class could add new data members, and the class member functions that used these data members wouldn't apply to the base class.
Dynamic Cast: A cast is an operator that converts data from one type to another type. In C++, dynamic casting is mainly used for safe downcasting at run time. To work on dynamic_cast there must be one virtual function in the base class.
The Downcasting is an opposite process to the upcasting, which converts the base class's pointer or reference to the derived class's pointer or reference. It manually cast the base class's object to the derived class's object, so we must specify the explicit typecast.
A dynamic cast expression is used to cast a base class pointer to a derived class pointer. This is referred to as downcasting.
Ok, I'll probably get shred into pieces for this answer...
Obviously, as the other answers stated this is undefined behaviour, as found in the standard. But if your Base
class has standard layout and your DerivedForInt
class does not add new data members it will have the same (standard) layout.
Under these conditions your cast should cause no troubles even it being UB. According to one of the sources, it is at least safe to do a
DerivedForInt *derived = reinterpret_cast<DerivedForInt*>(&base.a);
Sources:
What are Aggregates and PODs and how/why are they special?
PODs and inheritance in C++11. Does the address of the struct == address of the first member?
From the second link:
Here's the definition, from the standard section 9 [class]:
A standard-layout class is a class that:
- has no non-static data members of type non-standard-layout class (or array of such types) or reference,
- has no virtual functions (10.3) and no virtual base classes (10.1),
- has the same access control (Clause 11) for all non-static data members,
- has no non-standard-layout base classes,
- either has no non-static data members in the most derived class and at most one base class with non-static data members, or has no base classes with non-static data members, and
- has no base classes of the same type as the first non-static data member.
And the property you want is then guaranteed (section 9.2 [class.mem]):
A pointer to a standard-layout struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa.
This is actually better than the old requirement, because the ability to reinterpret_cast isn't lost by adding non-trivial constructors and/or destructor.
n3376 5.2.9/11
SinceA prvalue of type “pointer to cv1 B,” where B is a class type, can be converted to a prvalue of type “pointer to cv2 D,” where D is a class derived (Clause 10) from B if a valid standard conversion from “pointer to D” to “pointer to B” exists (4.10), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and B is neither a virtual base class of D nor a base class of a virtual base class of D. The null pointer value (4.10) is converted to the null pointer value of the destination type.
If the prvalue of type “pointer to cv1 B” points to a B that is actually a subobject of an object of type D, the resulting pointer points to the enclosing object of type D. Otherwise, the result of the cast is undefined.
&obj
is not points to DerivedForInt
it's UB.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With