Переопределение [[noreturn]] виртуальной функции

[[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, и у меня возникли проблемы с определением, являются ли атрибуты наследуемыми.

19

Решение

На практике ни г ++, лязг ни MSVC рассмотреть [[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» для всех трех компиляторов.

2

Другие решения

Я ознакомился со стандартом, и нет никаких признаков того, что либо [[noreturn]] в частности, или атрибуты в более общем смысле, «наследуются» переопределяющими функциями.

Трудно доказать отрицание, и стандарт фактически не объявляет это в любом случае, но, так как A::f() а также B::f() все еще являются различными функциями, и единственное описанное поведение определено в терминах функций, я думаю, что вы можете отметить A::f() как [[noreturn]],

При этом я не могу себе представить, какую полезную оптимизацию мог бы выполнить компилятор, учитывая динамическую диспетчеризацию.

8

Подумайте, что вы на самом деле говорите:

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

Конечно, человеческий читатель и, возможно, компилятор интерпретируют
это как «контракт», т.е.
«f() не вернусь, я обещаю

Это должно затем применяться к переопределениям f() или вы нарушаете контракт.

Стандарт может быть недостаточно конкретизирован по этому вопросу, но даже если он работает, я бы рекомендовал против него, основываясь на удобочитаемости.

3
По вопросам рекламы [email protected]