【发布时间】: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析构函数就是这样。derived的default(或隐式生成的)析构函数是public,即使在析构derived的过程中需要调用protected基类构造函数。
标签: c++ inheritance language-lawyer