Что говорит стандарт С++ об переопределении функции throw() с noexcept?

Следующее, похоже, компилируется на пару компиляторов, которые я пробовал:

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

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

Кажется, что можно переопределить функцию throw() с новой спецификацией noexcept. Я также пробовал наоборот (переопределяя noexcept с throw()), и, похоже, он работает. Почему это? Это поведение undefined или это разрешено?

Обратите внимание, что на генерацию кода влияет noexcept vs throw(). Они также не имеют эквивалентного поведения, поскольку noexcept вызывает другую функцию завершения, чем throw(). Идеальный ответ вызовет различия в поведении и почему они делают или не имеют значения в этом случае.

Ответ 1

Вы можете даже сделать это без переопределения:

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

[except.spec]/9 дает понять, что спецификация определения определяет, что происходит:

Всякий раз, когда возникает исключение типа E и поиск обработчика ([except.handle]) встречает внешний блок функции с исключение, которое не допускает E, затем

  • если определение функции имеет спецификацию динамического исключения, функция std::unexpected() называется ([except.unexpected]),

  • в противном случае функция std::terminate() называется ([Except.terminate]).

Это не проблема, потому что любая специальная обработка для этого происходит в вызываемом, а не вызывающем; все, кто должен знать, должен знать, что никакое исключение никогда не покинет эту функцию.

Ответ 2

Это поведение undefined или это разрешено? Это разрешено.

Из стандарта С++:

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

Практически: вы можете переопределить метод в производном классе и указать новые правила в дополнение к тем, которые заданы базовым классом.

В вашем примере throw() и noexcept эквивалентны.