The following code compiles and links with Visual Studio
(both 2017 and 2019 with /permissive-
), but does not compile with either gcc
or clang
.
foo.h
#include <memory> struct Base { virtual ~Base() = default; // (1) }; struct Foo : public Base { Foo(); // (2) struct Bar; std::unique_ptr<Bar> bar_; };
foo.cpp
#include "foo.h" struct Foo::Bar {}; // (3) Foo::Foo() = default;
main.cpp
#include "foo.h" int main() { auto foo = std::make_unique<Foo>(); }
My understanding is that, in main.cpp
, Foo::Bar
must be a complete type, because its deletion is attempted in ~Foo()
, which is implicitly declared and therefore implicitly defined in every translation unit that accesses it.
However, Visual Studio
does not agree, and accepts this code. Additionally, I've found that the following changes make Visual Studio
reject the code:
- Making
(1)
non-virtual - Defining
(2)
inline -- i.e.Foo() = default;
orFoo(){};
- Removing
(3)
It looks to me as if Visual Studio
does not define an implicit destructor everywhere it is used under the following conditions:
- The implicit destructor is virtual
- The class has a constructor that is defined in a different translation unit
Instead, it seems to only define the destructor in the translation unit that also contains the definition for the constructor in the second condition.
So now I'm wondering:
- Is this allowed?
- Is it specified anywhere, or at least known, that
Visual Studio
does this?
Update: I've filed a bug report https://developercommunity.visualstudio.com/content/problem/790224/implictly-declared-virtual-destructor-does-not-app.html. Let's see what the experts make of this.
struct BarDeleter { void operator()(Bar*) const noexcept; };
and change the unique_ptr tostd::unique_ptr<Bar, BarDeleter> bar_;
. Then in the implementation translation unit, add invoid Foo::BarDeleter::operator()(Foo::Bar* p) const noexcept { try { delete p; } catch(...) {/*discard*/}}
– Copyboy