为什么 gcc/clang 抱怨基 class 有一个受保护的析构函数,而不抱怨派生的 class?

Why do gcc/clang complain about the base class having a protected destructor, but not about the derived class?

以下代码使用 Visual Studio 2019 编译,但不使用 gcc 和 clang。定义变量 b 会导致后两个编译器出错。

#include <iostream>

class base
{
public:
    constexpr base(int i) : m_i(i) {}

    constexpr int i() const { return m_i; }

protected:
    ~base() = default; // Forbid polymorphic deletion
private:
    const int m_i;
};

class derived final : public base
{
public:
    constexpr derived() : base(42) {}
};

// Defining b compiles with Visual Studio 2019,
// but does not with gcc and clang.
// gcc 11.2:    error: 'base::~base()' is protected within this context
// clang 13.0:  error: variable of type 'const base' has protected destructor
constexpr base b(41);

// Defining d compiles with all 3 compilers.
constexpr derived d;

int main()
{
    std::cout << b.i() << std::endl;
    std::cout << d.i() << std::endl;
}

哪些编译器是正确的?编译程序的是微软,还是不编译程序的gcc和clang?

如果 gcc 和 clang 是正确的,为什么错误出现在 b 而不是 d?

更新:

FWIW,如果我如下更改 baseVisual Studio 2019 也不会编译程序:

class base
{
public:
    constexpr base(int i) : m_i(i) {}

    constexpr int i() const { return m_i; }

protected:
    //~base() = default; // Forbid polymorphic deletion

    // This does not compile with Visual Studio 2019 either.
    ~base() {}
private:
    const int m_i;
};

但据我所知,默认析构函数和手动实现的析构函数之间应该没有区别,所以是的,我猜这是 MS 编译器中的一个错误。

析构函数是一个成员函数,因此不能在无法调用其他成员函数的上下文中调用它。 调用的上下文是对象构造的上下文(见下文),因此在您的情况下,您不能在 base 之外调用 ~base() (或从 [= 派生的 class 11=]),这就是为什么 gcc 和 clang(它们是正确的)出现错误的原因。

[class.dtor#15] [...] In each case, the context of the invocation is the context of the construction of the object. [...]

错误出现在 b 而不是 d,因为 derived 的隐式声明的析构函数是 public:

[class.dtor#2] If a class has no user-declared prospective destructor, a prospective destructor is implicitly declared as defaulted ([dcl.fct.def]). An implicitly-declared prospective destructor is an inline public member of its class.

使 base 的析构函数默认为 protected 或 private 不会使子 classes 的析构函数默认为 protected 或 private,您只需获得隐式定义的析构函数,即 public.