【问题标题】:Overriding a [[noreturn]] virtual function覆盖 [[noreturn]] 虚函数
【发布时间】:2015-12-15 19:26:30
【问题描述】:

[[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 标准,但无法确定属性是否被继承。

【问题讨论】:

  • 我不知道,只是猜测,但是当您调用 B->f() 时,编译器很可能不知道 B 的动态类型是什么,所以我认为编译器将使用 noreturn 进行优化。我不知道为什么要进行这种设计,如果是因为 B 没有对此的实现,并且此 throw 是“不要调用它,仅在派生类中”的标志,那么用 noreturn 对其进行签名没有意义对我来说。 (嗯,设计本身也没有多大意义。:D)。如果您使用 D 的静态类型,我希望编译器不会优化到 noreturn。再说一遍:我只是猜的。

标签: c++ c++11 inheritance noreturn


【解决方案1】:

我已经了解了标准,没有迹象表明 [[noreturn]] 或更一般的属性是通过覆盖函数“继承”的。

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

话虽如此,考虑到动态调度,我无法想象编译器随后会执行哪些有用的优化。

【讨论】:

  • +1:属性不会被继承,因为它们将([标准措辞] “appertains”)应用于写入它们的实体。在 OP 示例中,[[noreturn]] 适用于 void B::f () - void D::f () 是一个完全不同的实体。
【解决方案2】:

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

#include <iostream>

struct B {
public:
  [[noreturn]] virtual void f() { std::cout << "B\n"; throw 0; }
};

struct D : public B {
  void f() override { std::cout << "D\n"; }
};

int main() 
{
    try { B{}.f(); } catch(...) {}
    D{}.f();

    B* d = new D{};
    d->f();
}

为所有三个编译器打印出“B”、“D”和“D”。

【讨论】:

  • 既然从noreturn 函数返回的是UB,那么预期的行为是什么?
【解决方案3】:

考虑一下你实际上在说什么:

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

当然,人类读者,可能还有编译器,会解释 这是一份“合同”,即
f() 不会返回,我保证”

这也应该适用于 f() 的覆盖,否则您将违反合同。

标准可能在这方面没有明确规定,但即使它有效,我也会根据可读性建议反对它。

【讨论】:

  • 对我来说,[[noreturn] 指定一个协定并不比基类的访问说明符指定一个协定更多。即,派生类中函数覆盖的公共/受保护/私有访问独立于基类中的访问。然而,对我来说,关键与你的最后一句话有关。由于标准没有指定它,所以它属于未指定的行为,因此我不应该依赖它。
  • @KyleKnoepfel 合约是在调用者和被调用者之间,而不是在基础和派生之间。访问说明符可以被认为是该合同的一部分。 public: void f() 承诺 b-&gt;f() 将始终可访问,并且即使 fD 中的私有函数覆盖,该承诺也将成立。一个更好的论点是,[[noreturn]] 不再是合约的一部分,就像纯虚函数的 =0 一样。
猜你喜欢
  • 2013-10-04
  • 2021-09-30
  • 2015-06-17
  • 2011-11-23
  • 2020-08-21
  • 1970-01-01
  • 1970-01-01
  • 2015-04-20
  • 2022-01-13
相关资源
最近更新 更多