Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a compiler place the implementation of an implicitly declared virtual destructor in a single separate translation unit?

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; or Foo(){};
  • 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.

like image 739
Mark Avatar asked Oct 22 '19 14:10

Mark


1 Answers

I believe this is a bug in MSVC. As for the std::default_delete::operator(), the Standard says that [unique.ptr.dltr.dflt/4]:

Remarks: If T is an incomplete type, the program is ill-formed.

Since there is no "no diagnostic required" clause, a conforming C++ compiler is required to issue a diagnostic [intro.compliance/2.2]:

If a program contains a violation of any diagnosable rule or ..., a conforming implementation shall issue at least one diagnostic message.

together with [intro/compliance/1]:

The set of diagnosable rules consists of all syntactic and semantic rules in this document except for those rules containing an explicit notation that “no diagnostic is required” or which are described as resulting in “undefined behavior”.


GCC uses static_assert to diagnose the type completeness. MSVC seemingly does not perform such a check. If it silently passes a parameter of std::default_delete::operator() to delete, then, this causes undefined behavior. Which might correspond with your observation. It may work, but until it is guaranteed by the documentation (as a non-standard C++ extension), I wouldn't use it.

like image 163
Daniel Langr Avatar answered Oct 15 '22 20:10

Daniel Langr