C++ Logo

std-discussion

Advanced search

Re: Clarification on virtual destruction order with non-static data member destructors

From: Abhinav Agarwal <abhinavagarwal1996_at_[hidden]>
Date: Thu, 26 Mar 2026 14:57:50 -0700
Yes, this looks like a compiler defect in MSVC. It appears to skip the vptr
adjustment when the destructor is = default.


On Thu, Mar 26, 2026 at 2:33 PM Anton Korzhuk via Std-Discussion
<std-discussion_at_[hidden]> wrote:
>
> Hello,
>
> I am interested in what the proper conformant behavior of the following code would be. GCC and Clang print "Base" here, but MSVC prints "Derived", at least when the Base destructor is declared as "= default"; declaring it with an empty body exhibits behavior identical to GCC and Clang.
>
> // === //
>
> #include <cstdio>
>
> class Base;
>
> class Member {
> Base *m_owner;
>
> public:
> explicit Member(Base *owner);
> ~Member();
> };
>
> class Base {
> Member m_mem;
>
> public:
> Base() : m_mem(this) {}
>
> // this prints Base on GCC, Clang and MSVC
> // virtual ~Base() {}
>
> // this prints Derived on MSVC, Base on GCC and Clang
> virtual ~Base() = default;
>
> virtual void test() { std::printf("Base\n"); }
> };
>
> class Derived : public Base {
> public:
> virtual ~Derived() {}
>
> virtual void test() override { std::printf("Derived\n"); }
> };
>
> Member::Member(Base *owner) : m_owner(owner) {}
> Member::~Member() {
> m_owner->test();
> }
>
> int main() {
> Derived d;
>
> return 0;
> }
>
> // === //
>
> Godbolt link: https://godbolt.org/z/oWGaxW5xs
>
> [class.cdtor] p4 isn't exactly clear to me on whether this behavior is well-defined.
>
> If I am understanding this correctly, would imply that Base::test() must be called during Member destruction -- "When a virtual function is called directly or indirectly from a constructor or from a destructor, *including during the construction or destruction of the class's non-static data members*, or [...], and the object to which the call applies is the object (call it x) under construction or destruction, the function called is the final overrider in the constructor's or destructor's class and not one overriding it in a more-derived class."
>
> However, I am not sure if the following part applies here -- "If the virtual function call uses an explicit class member access ([expr.ref]) and the object expression refers to the complete object of x or one of that object's base class subobjects but not x or one of its base class subobjects, the behavior is undefined.". Does it only make the case listed in the example from the standard (calling a virtual member function through a sibling pointer in the inheritance graph in a ctor/dtor) undefined, or are there any other edge-cases here that I don't see that might cover this too?
>
> Is there something else in the standard that may affect this as well?
>
> Is Microsoft Visual C++ behavior conformant here? If not, is this a compiler defect that should be reported to the Microsoft team, or is it something that also needs better wording in the standard and needs a DR for it?
>
> Thank you.
>
> --
> Std-Discussion mailing list
> Std-Discussion_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion

Received on 2026-03-26 21:58:19