This is an unofficial snapshot of the ISO/IEC JTC1 SC22 WG21 Core Issues List revision 116a. See http://www.open-std.org/jtc1/sc22/wg21/ for the official list.
2024-12-19
(From submission #478.)
Consider:
struct B {}; struct D1 : virtual B {}; struct D2 : virtual B {}; struct D : D1, D2 {}; void f() { D *d = new D; static_cast<D2&>(*d).~D2(); }
According to 11.4.7 [class.dtor] paragraph 13, invoking a destructor for a most derived object is different from invoking it for a base class subobject:
After executing the body of the destructor and destroying any objects with automatic storage duration allocated within the body, a destructor for class X calls the destructors for X's direct non-variant non-static data members, the destructors for X's non-virtual direct base classes and, if X is the most derived class (11.9.3 [class.base.init]), its destructor calls the destructors for X's virtual base classes.
However, there is no means to convey the difference in an explicit destructor call. As an aside, potentially-overlapping subobjects cannot be transparently replaced, thus any attempt at replacement would implicitly end the lifetime of the complete object due to storage reuse.
Possible resolution:
Split 7.6.1.3 [expr.call] paragraph 4 and amend, as follows:
..., even if the type of the function actually called is different.
If the postfix-expression P names a destructor or a pseudo-destructor, the postfix-expression is a possibly-parenthesized class member access. If P names a destructor, the behavior is undefined if the object expression denotes a base class subobject and that base class is or has a virtual base class or a virtual function (11.4.7 [class.dtor]). If
the postfix-expressionP names a pseudo-destructor(in which case the postfix-expression is a possibly-parenthesized class member access), the function call destroys the object of scalar type denoted by the object expression of the class member access (7.6.1.5 [expr.ref], 6.7.4 [basic.life]).