33

以下代码在 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上看到了类似的问题,但我没有看到参考标准的答案。

4

1 回答 1

37

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

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

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

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

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

(3)当一个特殊成员(比如析构函数)被显式默认时,即当你使用 时= default,异常规范是可选的(见下文“可能有”的使用):

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

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

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

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

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

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

于 2012-07-16T03:27:51.303 回答