覆盖虚函数[[noreturn]]

Overriding a [[noreturn]] virtual function

本文关键字:noreturn 函数 覆盖      更新时间:2023-10-16

[[noreturn]]属性可以应用于不打算返回的函数。例如:

[[noreturn]] void will_throw() { throw std::runtime_error("bad, bad, bad ...."); }

但是我遇到了以下情况(不,这不是我设计的):

class B {
public:
  virtual void f() { throw std::runtime_error(""); }
};
class D : public B {
  void f() override { std::cout << "Hi" << std::endl; }
};

我真的想把属性[[noreturn]]放在B::f()声明上。但是我不清楚派生类中的重写会发生什么。从[[noreturn]]函数成功返回会导致未定义的行为,如果重写也继承了该属性,我当然不希望出现这种情况。

问题:通过重写[[noreturn] virtual void B::f(),我继承[[noreturn]]属性吗?

我已经通读了c++ 14标准,但我在确定属性是否继承时遇到了麻烦。

我已经仔细检查了标准,没有迹象表明[[noreturn]]或更一般的属性是通过重写函数"继承"的。

很难证明是否定的,标准实际上并没有声明这两种方式,但是,由于A::f()B::f()仍然是不同的函数,唯一描述的行为是根据函数定义的,我认为你可以安全地将A::f()标记为[[noreturn]]

话虽如此,我无法想象编译器随后可以执行什么有用的优化,给定动态分派。

在实践中,g++clangMSVC都不认为[[noreturn]]属性是继承的

#include <iostream>
struct B {
public:
  [[noreturn]] virtual void f() { std::cout << "Bn"; throw 0; }
};
struct D : public B {
  void f() override { std::cout << "Dn"; }
};
int main() 
{
    try { B{}.f(); } catch(...) {}
    D{}.f();
    B* d = new D{};
    d->f();
}

为所有三个编译器输出"B", "D"answers"D"

考虑你实际上在说什么:

class B
{
public:
    [[noreturn]] virtual void f() { throw std::runtime_error(""); }
};

当然,人类读者,可能还有编译器,会解释这是一个"契约",即
"f()不会返回,我保证"

这也应该适用于f()的重写,否则你就违反了合同。

这个标准可能没有详细说明,但即使它有效,基于可读性,我也不建议使用。