【问题标题】:gcc exception specification of default destructor默认析构函数的 gcc 异常规范
【发布时间】:2011-09-26 05:02:34
【问题描述】:
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 (prerelease) )上,但在以前的 gcc 版本上编译没有错误(我的 debian 系统上的 4.5 再次)。

How does an exception specification affect virtual destructor overriding? 根据该答案,编译器应该创建一个与基类的 throw 声明匹配的默认构造函数。显然这不是在新 gcc 上发生的事情。发生了什么变化,正确的编译器行为是什么,除了在派生类中手动添加空析构函数(例如编译器标志)之外,是否有一些简单的解决方案。

【问题讨论】:

  • 那是实际的代码吗?如果您跳过了成员声明,​​那可能会改变结果。另外,您是否指定了 C++11 支持?规则在析构函数方面略有改变,虽然代码应该仍然没问题,但那里可能有错误。
  • 不,这不是实际代码。我能说出来,因为它至少有一个语法错误。 @Yordan,请在提问时发布实际的、可编译的代码。有关如何执行此操作以及为什么重要的详细信息,请参阅sscce.org
  • @Rob - O.o 你在开玩笑吧..?这不是一些复杂而冗长的源代码的问题,这些东西很重要。缺少两个明显的分号,这不会改变问题、信息、环境等任何东西。
  • @Kiril:我 100% 同意 Rob。如果一个 OP 在发布的代码中引入了新的错误,我们怎么能期望在原始代码中找到错误呢?
  • 在这种情况下,一个完整的、可编译的示例是双重重要的,因为如果他将一个成员添加到他为简单起见而删除的派生类,无论错误是他的错还是编译器的错误都可以改变。就目前而言,该错误是编译器错误。

标签: c++ linux exception gcc throw


【解决方案1】:

我认为在实际代码中 ~A()~B() 被声明为虚拟的? (错误消息是在抱怨虚拟析构函数,但在编写的代码中,没有一个析构函数是虚拟的。)

我相信虚拟继承是触发您的问题的原因。 C 的(隐式定义的)析构函数需要首先调用~B(),然后,因为C 是派生最多的类,所以调用~A()。 (12.4/6)

~C() 生成的异常规范需要允许任何异常传播,因为它直接调用没有异常规范的~A()。 (15.4/13)

然后触发您的错误 - 您不能使用可能抛出的版本覆盖带有 throw() 规范(B 的析构函数)的虚函数。 (15.4/3)

解决方案是将throw() 放在A 的析构函数上。 (如果你不能这样做,那你为什么要在 B 上这样做?)

如果没有虚拟继承,错误也不会发生 - 因为那时 C 的析构函数只会调用 B 的析构函数。 (B 的析构函数仍会调用 A 的 - 而你仍然如履薄冰,因为如果 A 的析构函数抛出你将直接​​进入 terminate()。)

【讨论】:

  • 感谢您的回答!关于 A 有一个我错过的虚拟析构函数,你是对的(我现在在示例中更正了这一点)。看起来在您提供的解决方案中,将 throw() 添加到 A 是适合我的情况的解决方案。
  • 更好的解决方案是根本不使用throw 规范。该规范现在被广泛视为语言中的错误。
【解决方案2】:

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

按照我的理解,如果 B 类有一个显式没有抛出任何东西的析构函数(即。

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

应该没问题。

不管怎样,我上次检查时从 d'tors 抛出异常是非常糟糕的做法。

希望对你有所帮助!

【讨论】:

  • 声明异常规范也是一种糟糕的做法
  • “除了在派生类中手动添加空析构函数之外,是否有一些简单的解决方案”。在析构函数中添加 throw()(注意 - 它是 empty 抛出说明符(类似于 C++0x 中的 nothrow))与仅使用抛出说明符不同。这不是一个坏习惯。甚至推荐 - 析构函数不应该抛出,对吧? (:
  • 我不确定这是否可能......但据我所知,事情不应该被扔掉。这意味着每次在 try/catch 中删除对象时都必须进行包装。我会说:不——但这对我来说似乎是在滥用投掷机制,所以我不能确定当你以一种与另一种相反的方式滥用它时会发生什么。虽然没有,但在阅读了 C++ 规范中 OP 帖子的摘录之后,如果不指定与基类的覆盖函数相同的异常类型,就无法做到这一点。我认为这与指针类型的歧义有关。
猜你喜欢
  • 1970-01-01
  • 2017-02-28
  • 1970-01-01
  • 2013-06-15
  • 2017-01-09
  • 2013-08-04
  • 1970-01-01
  • 1970-01-01
  • 2020-11-09
相关资源
最近更新 更多