Dynamic cast in destructor
Maybe I found the solution myself, the reply is no it's not possible:
From bullet 6 of cppreference.com documentation:
When dynamic_cast is used in a constructor or a destructor (directly or indirectly), and expression refers to the object that's currently under construction/destruction, the object is considered to be the most derived object. If new-type is not a pointer or reference to the constructor's/destructor's own class or one of its bases, the behavior is undefined.
See also [class.cdtor]/6 of the standard.
Since I'm casting to Base1 in Base2 destructor, this behavior is undefined.
I agree with @j6t's answer, but here is an expanded reasoning with standard references.
The special behavior of dynamic_cast
for objects under construction and destruction is described by [class.cdtor]/5 of the C++17 standard (final draft) and equivalently by previous standard versions.
In particular it says:
When a
dynamic_cast
is used [...] in a destructor, [...], if the operand of thedynamic_cast
refers to the object under construction or destruction, this object is considered to be a most derived object that has the type of the [...] destructor's class. If the operand of thedynamic_cast
refers to the object under [...] destruction and the static type of the operand is not a pointer to or object of the [...] destructor's own class or one of its bases, the dynamic_cast results in undefined behavior.
The undefined behavior does not apply here, since the operand is the expression this
, which trivially has the type of a pointer to the destructor's own class since it appears in the destructor itself.
However, the first sentence states that the dynamic_cast
will behave as if *this
was a most derived object of type Base2
and therefore the cast to Base1
can never succeed, because Base2
is not derived from Base1
, and dynamic_cast<Base1*>(this)
will always return a null pointer, resulting in the behavior you are seeing.
cppreference.com states that the undefined behavior happens if the destination type of the cast is not the type of the destructor's class or one of its bases, rather than having this apply to the operands type. I think that is just a mistake. Probably the mention of "new-type" in bullet point 6 was supposed to say "expression", which would make it match my interpretation above.
The dynamic_cast
is well-defined in this situation. It is correct that you observe both lines of output.
You are wrong to assume that in the destructor of Base2
this
is a derived class. At this time, the derived class part has already been destroyed, so it cannot be a derived class anymore. In fact, at the time when the destructor of Base2
runs, the object pointed to by this
is only a Base2
object. Since Base2
is not related to Base1
in any way, the dynamic_cast
returns a null pointer, and the conditional is entered accordingly.
Edit: The standard says:
When a
dynamic_cast
is used in a constructor [...] or in a destructor [...], if the operand of thedynamic_cast
refers to the object under construction or destruction, this object is considered to be a most derived object that has the type of the constructor or destructor's class. If the operand of thedynamic_cast
refers to the object under construction or destruction and the static type of the operand is not a pointer to or object of the constructor or destructor's own class or one of its bases, thedynamic_cast
results in undefined behavior.
The operand this
refers to the object under destruction. Therefore, the class of the destructor (Base2
) is considered the most-derived class, and that is the reason why the object is not related to the destination type (Base1*
) in any way. Furthermore, the static type of the operand this
is Base2* const
, which clearly is a pointer to the destructor's own class. Therefore, the rule about undefined behavior does not apply. In summary, we have well-defined behavior.