编译器在这里面陷入了两难境地,原因如下:
1. 在函数声明中
不指定任何异常(即不使用throw或noexcept(相当于noexcept(true)))意味着允许该函数抛出
所有可能的异常:
(§15.4/12, 强调我的)没有异常规范或形式为noexcept(constant-expression)的异常规范,其中constant-expression产生false允许所有异常。
2.
默认析构函数必须允许其隐式定义直接调用的函数允许的异常:
(§15.4/14, 强调我的)隐式声明的特殊成员函数(Clause 12)应具有异常规范。如果f是一个隐式声明的默认构造函数、复制构造函数、移动构造函数、
析构函数、复制赋值运算符或移动赋值运算符,则它的隐式异常规范只有当T被允许通过f的隐式定义直接调用的函数的异常规范时才指定类型标识符T;如果任何函数直接调用f允许所有异常,则f应允许所有异常,如果每个函数直接调用f允许没有异常,则f不应允许任何异常。
3. 当特殊成员(如析构函数)被显式地设置为默认值时,即当您使用= default时,异常规范是
可选的(请参见下面的"
可能具有"):
(8.4.2/2, 强调我的)显式默认函数[...]只有在与隐式声明上的异常规范兼容(15.4)时,才能具有显式异常规范。[...]
标准中没有要求在显式默认析构函数中指定异常规范。
结论:因此,
不指定显式默认析构函数的异常可以有两种解释:
1. 意味着
允许所有异常(根据上述1)。
2. 或者,作为替代方案,意味着允许与隐式默认析构函数允许的相同异常一样允许的异常(根据上述3),在您的情况下意味着允许
没有异常(根据上述2)。
很遗憾,在您的基类声明的情况下,GCC会以一种方式(支持“没有异常”)解决这个困境,并以不同的方式在派生类的情况下(支持“所有异常”)。
我认为这种明显含糊不清的情况的最自然解释是假定(2)和(3)覆盖(1)。虽然标准没有如此规定,但它应该这样做。根据这种解释,Clang 在这里似乎是正确的。