【问题标题】:Default destructor nothrow默认析构函数 nothrow
【发布时间】:2012-07-16 01:50:46
【问题描述】:

以下代码在gcc-4.7.1下不能编译,而是在clang-3.2下编译。哪一个遵循 C++11 标准?

struct X {
  virtual ~X() = default;
};

struct Y : X {
  virtual ~Y() = default;
};

gcc-4.7.1 抱怨:

looser throw specifier for 'virtual Y::~Y()'
error: overriding 'virtual X::~X() noexcept(true)'

显然,gcc-4.7.1 认为 X 的默认析构函数不是 nothrow,但 Y 的默认析构函数不是 nothrow。为什么是这样?任何人都可以参考标准中的正确位置吗?谢谢。

我在 stackoverflow 上看到了类似的问题,但没有看到参考标准的答案。

【问题讨论】:

  • 如果您的代码与问题中的代码完全相同,则很可能是编译器中的错误。
  • 仅供参考 g++ 快照 20120708 正确编译代码。
  • 很好的问题,今天在尝试在 Debian g++4.7 上移植某些东西时遇到了这个问题

标签: c++ c++11


【解决方案1】:

编译器陷入两难境地,原因如下:

(1) 不指定任何异常 在函数声明中(即不使用thrownoexcept(相当于noexcept(true)))意味着允许该函数抛出所有可能的例外情况:

(第 15.4/12 节,强调我的)没有异常规范或具有noexcept(constant-expression) 形式的异常规范的函数,其中constant-expression 产生false 允许所有例外。 [...]

(2) 默认析构函数必须完全允许由其隐式定义直接调用的函数所允许的异常:

(第 15.4/14 节,强调我的)隐式声明的特殊成员函数(第 12 条)应具有异常规范。 如果 f 是隐式声明的默认构造函数、复制构造函数、移动构造函数、析构函数、复制赋值运算符或移动赋值运算符,其隐式异常规范指定type-id T 当且仅当 T 被 f 的隐式定义直接调用的函数的异常规范所允许;如果 f 直接调用的任何函数都允许所有异常,则 f 应允许所有异常,如果 f 直接调用的每个函数都不允许异常,则 f 应不允许异常。

(3) 当特殊成员(如析构函数)显式默认时,即当您使用= default时,异常规范是可选(参见在下面使用“可能有”):

(8.4.2/2,强调我的)一个显式默认函数 [...] 可能有一个明确的异常规范,只有当它兼容( 15.4)在隐式声明上带有异常规范。 [...]

标准中没有声明需要在显式默认析构函数中指定异常。

结论:因此,不指定显式默认析构函数的异常可以有两种解释:

  • 表示允许所有例外情况(根据上述(1))
  • 或者,或者,意味着允许与析构函数的隐式默认定义所允许的完全相同的异常(根据上面的(3)),在您的情况下意味着允许没有例外(根据上面的(2))。

不幸的是,在基类声明的情况下,GCC 以一种方式(支持“无例外”)解决了这个困境,而在派生类的情况下,GCC 以不同的方式解决了这个难题(支持“所有例外”那里)。

我相信对这种公认的模棱两可的情况最自然的解释是假设 (2) 和 (3) 覆盖 (1)。 标准没有这么说,但它应该这么说。在这种解释下,Clang 似乎就在这里。

【讨论】:

  • 很好的答案,这个问题实际上非常重要,因为您可以使用 CRTP 模式声明(像我一样)带有 =default 受保护析构函数的单例,然后所有 g++ 编译器 = 4.8,代码编译得很好。我对可移植代码的解决方案是将析构函数编写为virtual ~Destructor(){};。不优雅,但适用于 g++ 4.7 及更高版本。
猜你喜欢
  • 2012-07-14
  • 2012-12-24
  • 1970-01-01
  • 2012-04-14
  • 2017-10-03
  • 2022-01-09
  • 2014-01-30
  • 2010-10-24
  • 2017-04-21
相关资源
最近更新 更多