This is a variant of the questions Downcasting using the Static_cast in C++ and Safety of invalid downcast using static_cast (or reinterpret_cast) for inheritance without added members
I am not clear on the phrase in the standard "B that is actually a subobject of an object of type D, the resulting pointer points to the enclosing object of type D" with respect to behavior in ~B. If you cast to D in ~B, is it still a subobject at that point ? The following simple example shows the question:
void f(B* b);
class B {
public:
B() {}
~B() { f(this); }
};
class D : public B { public: D() {} };
std::set<D*> ds;
void f(B* b) {
D* d = static_cast<D*>(b); // UB or subobject of type D?
ds.erase(d);
}
I know that the cast is an open door to disaster, and doing anything like this from the dtor is a bad idea, but a co-worker claims "The code is valid and works correctly. That cast is perfectly valid. The comment clearly states that it should not be dereferenced".
I pointed out that the cast is unnecessary and we should prefer the protection provided by the type system to comments. The sad part is that he is one of the senior/lead developers and a supposed c++ "expert".
Can I tell him the cast is UB ?
[19.4] Is it OK to convert a pointer from a derived class to its base class? Yes.
Deleting a derived class object using a pointer of base class type that has a non-virtual destructor results in undefined behavior. To correct this situation, the base class should be defined with a virtual destructor. For example, following program results in undefined behavior.
The static_cast operator converts variable j to type float . This allows the compiler to generate a division with an answer of type float . All static_cast operators resolve at compile time and do not remove any const or volatile modifiers.
No. You never need to explicitly call a destructor (except with placement new). A derived class's destructor (whether or not you explicitly define one) automagically invokes the destructors for base class subobjects. Base classes are destructed after member objects.
[expr.static.cast]/p11:
A prvalue of type “pointer to cv1
B
,” where B is a class type, can be converted to a prvalue of type “pointer to cv2D
,” whereD
is a class derived (Clause 10) fromB
, if a valid standard conversion from “pointer toD
” to “pointer toB
” exists (4.10), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, andB
is neither a virtual base class ofD
nor a base class of a virtual base class ofD
. The null pointer value (4.10) is converted to the null pointer value of the destination type. If the prvalue of type “pointer to cv1B
” points to aB
that is actually a subobject of an object of typeD
, the resulting pointer points to the enclosing object of typeD
. Otherwise, the behavior is undefined.
The question, then, is whether, at the time of the static_cast
, the pointer actually points to "a B
that is actually a subobject of an object of type D
". If so, there is no UB; if not, then the behavior is undefined whether or not the resulting pointer is dereferenced or otherwise used.
[class.dtor]/p15 says that (emphasis mine)
Once a destructor is invoked for an object, the object no longer exists
and [basic.life]/p1 says that
The lifetime of an object of type
T
ends when:
- if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or
- [...]
From this, then, the D
object's lifetime has ended as soon as its destructor is invoked, and certainly by the time B
's destructor began to execute - which is after D
's destructor body has finished execution. At this point, there is no "object of type D
" left that this B
can be a subobject of - it "no longer exists". Thus, you have UB.
Clang with UBsan will report an error on this code if B
is made polymorphic (given a virtual function), which supports this reading.
Apparently your co-worker is under the impression that as long as you do not dereference an invalid pointer, you are fine.
He is wrong.
Merely evaluating such a pointer has undefined behaviour. This code is obviously broken.
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