默认析构函数nothrow

Kan*_* Li 32 c++ c++11

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

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

struct Y : X {
  virtual ~Y() = default;
};
Run Code Online (Sandbox Code Playgroud)

gcc-4.7.1抱怨说:

looser throw specifier for 'virtual Y::~Y()'
error: overriding 'virtual X::~X() noexcept(true)'
Run Code Online (Sandbox Code Playgroud)

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

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

jog*_*pan 37

由于以下原因,编译器陷入两难境地:

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

(§15.4/ 12,强调我的)一种没有例外规定的函数,或者具有异常规范的函数,noexcept(constant-expression)其中constant-expression产量false 允许所有异常.[...]

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

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

(3)当一个特殊成员(例如析构函数)被明确默认时,即当你使用时= default,异常规范是可选的(参见下面" 可能有" 的使用):

(8.4.2/2,强调我的)明确默认的函数 [...] 只有在与隐式声明的异常规范兼容(15.4)时才有明确的异常规范.[...]

标准中没有声明需要在显式默认的析构函数中使用异常规范.

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

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

不幸的是,在你的基类声明的情况下,GCC以一种方式(支持"无例外")解决了这种困境,并且在派生类的情况下以不同的方式解决了这种困境(支持那里的"所有例外").

我认为对这种公认的模糊情况的最自然的解释是假设(2)和(3)重写(1).标准没有这样说,但应该如此.根据这种解释,Clang似乎就在这里.

  • 很好的答案,这个问题实际上非常重要,因为您可以使用CRTP模式声明(就像我所做的)带有`= default` protected析构函数的单例,然后所有g ++编译器<= 4.7将吐出相同的错误"更宽松的投掷......"但是对于g ++> = 4.8,代码编译得很好.我对可移植代码的解决方案是将析构函数编写为`virtual~Destructor(){};`.不优雅,但适用于g ++ 4.7及更高版本. (3认同)