Date: Thu, 26 Mar 2026 22:33:00 +0100
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.
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.
Received on 2026-03-26 21:33:09
