编译器可以将隐式声明的虚拟析构函数的实现放在单个单独的转换单元中吗?

Mar*_*ark 8 c++ visual-c++ language-lawyer delete-operator incomplete-type

以下代码使用编译和链接Visual Studio(2017和2019都使用/permissive-),但不使用gcc或进行编译clang

foo.h

#include <memory>

struct Base {
    virtual ~Base() = default; // (1)
};

struct Foo : public Base {
    Foo();                     // (2)
    struct Bar;
    std::unique_ptr<Bar> bar_;
};
Run Code Online (Sandbox Code Playgroud)

foo.cpp

#include "foo.h"

struct Foo::Bar {};            // (3)
Foo::Foo() = default;
Run Code Online (Sandbox Code Playgroud)

main.cpp

#include "foo.h"

int main() {
    auto foo = std::make_unique<Foo>();
}
Run Code Online (Sandbox Code Playgroud)

我的理解是,in main.cpp中的Foo::Bar必须是完整类型,因为in中尝试将其删除~Foo(),它是隐式声明的,因此在访问它的每个翻译单元中都是隐式定义的。

但是,Visual Studio不同意,并接受此代码。此外,我发现以下更改使Visual Studio代码遭到拒绝:

  • 使(1)非虚拟
  • 定义(2)内联-即Foo() = default;Foo(){};
  • 拆下 (3)

在我看来,Visual Studio在以下情况下使用它的地方似乎都没有定义隐式析构函数:

  • 隐式析构函数是虚拟的
  • 该类具有在不同翻译单元中定义的构造函数

取而代之的是,它似乎仅在转换单元中定义了析构函数,该单元中还包含第二种条件下的构造函数定义。

所以现在我想知道:

  • 可以吗?
  • 是否在任何地方(或至少是已知的)进行了指定Visual Studio

更新:我已经提交了错误报告https://developercommunity.visualstudio.com/content/problem/790224/implictly-declared-virtual-destructor-does-not-app.html。让我们看看专家对此的看法。

Dan*_*ica 2

我相信这是 MSVC 中的一个错误。至于std::default_delete::operator(),标准说[unique.ptr.dltr.dflt/4]

\n\n
\n

备注:如果 T 是不完整类型,则程序格式错误

\n
\n\n

由于没有“不需要诊断”子句,因此需要符合要求的 C++ 编译器来发出诊断 [intro.compliance/2.2]

\n\n
\n

如果程序包含违反任何可诊断规则的行为或……,符合要求的实现应发出至少一条诊断消息

\n
\n\n

[intro/compliance/1]一起:

\n\n
\n

可诊断规则集由本文档中的所有句法和语义规则组成,但包含明确表示法\xe2\x80\x9cno 诊断需要\xe2\x80\x9d 或被描述为导致\xe2\x80 的规则除外\x9c 未定义行为\xe2\x80\x9d。

\n
\n\n
\n\n

GCC 用于static_assert诊断类型完整性。MSVC 似乎不执行这样的检查。如果它默默地传递一个参数std::default_delete::operator()to delete,那么,这会导致未定义的行为。这可能与你的观察相符。它可能有效,但在文档保证它(作为非标准 C++ 扩展)之前,我不会使用它。

\n