【问题标题】:Why do gcc/clang complain about the base class having a protected destructor, but not about the derived class?为什么 gcc/clang 抱怨基类具有受保护的析构函数,而不抱怨派生类?
【发布时间】:2021-12-22 01:00:39
【问题描述】:

以下代码使用 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,如果我将base 更改如下,Visual 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 编译器中的一个错误。

【问题讨论】:

  • 我想说 GCC/Clang 在这种情况下是正确的。为什么?因为在b(或其派生类)的上下文之外实例化,或者更确切地说“清理”您的b 实例是不可能的。它受到保护。因此,您无法从“外部”破坏它。另一方面,d 有一个(隐式)公共析构函数,因此没有人抱怨。
  • 我认为 gcc 和 clang 是正确的,如果析构函数不是公共的,你不能在 base 之外实例化 base。你会发现很多关于私有/受保护的析构函数的问题/答案。 derived 的析构函数是公共的,因此可以实例化它(使基析构函数受保护不会使子类析构函数受保护)。
  • 您似乎发现了一个 MS 编译器错误。
  • VS2019 不正确。当构造一个对象时,它的析构函数需要是可访问的。对于文件范围内的变量,这意味着析构函数必须是public,因此需要关注b。对于派生类的实例,派生类必须可以访问基类析构函数——protected 析构函数就是这样。 deriveddefault(或隐式生成的)析构函数是public,即使在析构derived的过程中需要调用protected基类构造函数。

标签: c++ inheritance language-lawyer


【解决方案1】:

析构函数是一个成员函数,因此不能在您无法调用其他成员函数的上下文中调用它。 调用的上下文是对象构造的上下文(见下文),所以在你的情况下,你不能在base(或从base派生的类)之外调用~base(),这就是你得到的原因gcc 和 clang 的错误(正确)。

[class.dtor#15] [...] 在每种情况下,调用的上下文都是对象构造的上下文。 [...]

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

[class.dtor#2] 如果一个类没有用户声明的预期析构函数,则预期析构函数被隐式声明为默认值 ([dcl.fct.def])。 隐式声明的预期析构函数是其类的内联公共成员。

base的析构函数设为protected或private并不会使子类的析构函数默认为protected或private,你只需获取隐式定义的析构函数,即public。

【讨论】:

    猜你喜欢
    • 2012-09-19
    • 2016-02-27
    • 2011-01-21
    • 1970-01-01
    • 2017-01-17
    • 1970-01-01
    • 2021-06-27
    • 1970-01-01
    • 2016-10-30
    相关资源
    最近更新 更多