【问题标题】:What does the C++ standard say about overriding a throw() function with noexcept?C++ 标准对使用 noexcept 覆盖 throw() 函数有何规定?
【发布时间】:2017-03-06 16:39:40
【问题描述】:

以下似乎可以在我尝试过的几个编译器上编译:

class A
{
public:
    virtual void foo() throw() = 0;
};

class B : public A
{
public:
    virtual void foo() noexcept override { }
};

似乎可以使用更新的 noexcept 规范覆盖 throw() 函数。我也尝试了相反的方法(用 throw() 覆盖 noexcept),它似乎有效。这是为什么?这是未定义的行为还是允许的?

请注意,代码生成受 noexcept 与 throw() 的影响。它们也没有等效的行为,因为 noexcept 调用了与 throw() 不同的终止函数。一个理想的答案将指出行为差异以及在这种情况下它们为什么重要或不重要。

【问题讨论】:

  • 除非我遗漏了什么,否则stackoverflow.com/questions/5295134/… 似乎回答了这个问题。
  • 请参阅下面关于 Biagio 答案的主题。 throw() 和 noexcept 在行为上的细微差别使得这比仅仅限制抛出的异常更加棘手。

标签: c++ c++11


【解决方案1】:

这是未定义的行为还是允许的? 这是允许的。

来自 C++ 标准:

如果一个虚函数有一个异常规范,所有 任何覆盖的函数的声明,包括定义 任何派生类中的虚函数只允许异常 基类的异常规范所允许的 虚函数,除非覆盖函数被定义为 已删除。

实际上:您可以覆盖派生类中的方法,并在基类指定的规则之外指定新规则。

在您的示例中,throw() 和 noexcept 是等价的。

【讨论】:

  • 如何处理 noexcept 和 throw() 之间的行为差​​异? Noexcept 调用在出现意外异常时终止,而 throw() 调用意外。我发布的示例将调用哪个? Noexcept 不会展开堆栈,而 throw() 可能会展开(这会影响代码生成)。如何通过调用代码来解释规范中的这些差异,这些代码可能认为它正在调用的代码是 throw(),而实际上它是 noexcept。
  • @Cicada unexpected 默认会调用terminate。如果用户指定的行为不同,嗯我自己试试
  • @BiagioFesta - “未定义的行为”并不意味着“会做一些讨厌的事情”;这意味着只是语言定义没有告诉你发生了什么。
  • @Cicada 这个处理应该在被调用者中完成,而不是在调用者中。在任何一种情况下,都不会离开被调用的函数。
【解决方案2】:

您甚至可以在不覆盖的情况下执行此操作:

void f() throw();
void f() noexcept { throw 1; }

[except.spec]/9 明确表示定义中的规范控制了发生的情况:

每当抛出 E 类型的异常并搜索处理程序时 ([except.handle]) 遇到一个函数的最外层块 不允许 E 的异常规范,则,

  • 如果函数定义具有动态异常规范,则 函数std::unexpected()被调用([except.unexpected]),

  • 否则调用函数std::terminate() ([除了.terminate])。

这不是问题,因为对此进行的任何特殊处理都发生在被调用者中,而不是调用者中;调用者只需要知道函数不会有任何异常。

【讨论】:

  • 谢谢!这是有道理的。
猜你喜欢
  • 2017-09-02
  • 1970-01-01
  • 1970-01-01
  • 2017-01-09
  • 2015-03-29
  • 2012-03-14
  • 2014-08-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多