GCC默认析构函数的异常规范

8
class A
{
    public:
    virtual ~A()
    {
    }
};

class B : virtual public A
{
    public:
    ~B() throw()
    {}
};

class C : public B
{
};

int main(int argc, char * argv [])
{
return 0;
}

那段代码会产生以下错误:
error: looser throw specifier for ‘virtual C::~C()’
error:   overriding ‘virtual B::~B() throw ()’

在我的Debian测试环境下(gcc(Debian 4.6.0-10)4.6.1 20110526(预发布版)),但在先前的gcc版本(例如我Debian系统上的4.5)中编译时没有错误。根据{{link1:异常规范如何影响虚拟析构函数覆盖?}}的回答,编译器应该创建一个匹配基类throw声明的默认构造函数。显然,这不是新版gcc发生的情况。有什么改变,正确的编译器行为是什么,除了在派生类中手动添加空析构函数之外还有没有其他简单的解决方法(例如编译器标志)?

这是实际的代码吗?如果你忽略了成员声明,那会改变结果。另外,你是否指定了C++11支持?关于析构函数规则有些微小的变化,虽然代码应该还是可以运行的,但可能会出现错误。 - Dennis Zickefoose
4
不,那不是实际的代码。我可以看出它至少有一个语法错误。@Yordan,请在提问时发布实际的可编译代码。关于如何做到这一点以及为什么很重要,请参阅http://sscce.org。 - Robᵩ
1
@Rob - O.o 你一定是在开玩笑吧?这不是一个有着复杂和冗长源代码的问题,这些东西才重要。只是漏了两个明显的分号,这并不会改变问题、信息、环境或其他任何东西。 - Kiril Kirov
1
@Kiril:我完全同意Rob的看法。如果一个 OP 在发布的代码中引入了新错误,我们如何能够找到原始代码中的错误呢? - TonyK
在这种情况下,一个完整的、可编译的示例尤为重要,因为如果他为了简单起见从派生类中删除了一个成员,而后来又添加了该成员,那么错误是他的错还是编译器的错就会发生变化。目前,这个错误是编译器的一个 bug。 - Dennis Zickefoose
@Rob,@Dennis,@TonyK,我因为这个不起作用的例子而受到了一些批评。尝试提供一个真实的例子来说明,结果发现情况有点更加复杂。请看一下。 - Yordan Pavlov
2个回答

3
我认为在实际代码中,~A()~B()中的一个被声明为虚函数?(错误消息抱怨虚析构函数,但在写入的代码中,没有任何析构函数是虚的。)
我认为虚继承是触发问题的原因。C的(隐式定义的)析构函数要求首先调用~B(),然后,因为C是最派生的类,再调用~A()。(12.4/6)
对于~C()的生成异常说明需要允许任何异常传播,因为它直接调用没有异常说明的~A()。(15.4 / 13)
这就触发了您的错误 - 您不能使用可能引发异常的版本(B的析构函数)覆盖带有throw()说明符的虚函数。(15.4/3)
解决方法是在A的析构函数上放置throw()。(如果您无法这样做,那么为什么要在B上这样做?)
如果没有虚继承,也不会发生错误-因为然后C的析构函数只会调用B的析构函数。(B的析构函数仍将调用A的析构函数-而且您仍在薄冰上滑行,因为如果A的析构函数抛出异常,您将直接到达terminate()。)

感谢您的回答!您关于A具有虚拟析构函数的说法是正确的,这是我所忽略的(我已在示例中进行了更正)。从您提供的解决方案中看来,对于我的情况,将throw()添加到A是正确的选择。 - Yordan Pavlov
更好的解决方案是完全不使用 throw 规定。这个规定现在被广泛认为是语言上的一个错误。 - David Hammen

0

GCC 4.X比以前的版本更严格,因此可能不会隐式地声明。尝试明确说明。

我理解的方式是,如果B类有一个显式抛出无内容的析构函数(即

class B: public A
{
public:
virtual ~B() throw { }
}

应该没问题。

无论如何,我上次检查时发现从析构函数中抛出异常是非常不好的做法。

希望能对你有所帮助!


将异常规范声明为函数的一部分是一个不好的做法。 - BЈовић
除了在派生类中手动添加空析构函数之外,有没有一些简单的解决方案来解决这个问题?此外,在析构函数中添加throw()(注意-它是空的throw说明符(类似于C ++ 0x中的nothrow))不同于仅使用throw说明符。这不是一个坏习惯。甚至建议这样做-析构函数不应该抛出异常,对吧?(: - Kiril Kirov
我不确定是否可能,但据我所知,东西不应该被扔出d'tors。这意味着每次删除对象时都必须在try/catch中包装。我会说:不行-但是这看起来像是滥用抛出机制,所以我不能确定滥用它的一种方式与另一种方式相比会发生什么。尽管如此,在阅读了C++规范中OP帖子的摘录后,没有办法在不指定与基类覆盖函数相同的异常类型的情况下执行它。我认为这与指针类型的歧义有关。 - Syndacate

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接