18 Exception handling [except]

18.4 Exception specifications [except.spec]

Предикат, указывающий, не может ли функция выйти из-за исключения, называется предикатом exception specification функции. Если предикат ложен, функция имеет potentially-throwing exception specification, в противном случае - non-throwing exception specification. Спецификация исключения определяется неявно или явно с помощью noexcept-specifier суффикса a function declarator.

noexcept-specifier:
	noexcept ( constant-expression )
	noexcept
	throw ( )

В a noexcept-specifier, constant-expressionесли указано , должно быть a contextually converted constant expression of type bool; это постоянное выражение является спецификацией исключения для типа функции, в которой noexcept-specifierпоявляется. Следующий ( токен noexcept является частью noexcept-specifierи не является началом initializer. noexcept-specifier noexcept Без constant-expression эквивалентно noexcept-specifier noexcept(true). noexcept-specifier throw() Это deprecatedи эквивалентно noexcept-specifier noexcept(true).

Если объявление функции не имеет noexcept-specifier, то объявление имеет спецификацию потенциально генерирующего исключения, если только оно не является деструктором или функцией освобождения или не используется по умолчанию в своем первом объявлении, и в этих случаях спецификация исключения такая, как указано ниже, и никакое другое объявление для этой функции должен быть noexcept-specifier. В explicit instantiation a noexcept-specifierможет быть указано, но не обязательно. Если a noexcept-specifierуказан в явной директиве создания экземпляра, спецификация исключения должна быть такой же, как спецификация исключения всех других объявлений этой функции. Диагностика требуется только в том случае, если спецификации исключений не совпадают в одной единице трансляции.

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

struct B {
  virtual void f() noexcept;
  virtual void g();
  virtual void h() noexcept = delete;
};

struct D: B {
  void f();                     // ill-formed
  void g() noexcept;            // OK
  void h() = delete;            // OK
};

Объявление D​::​f неправильно сформировано, потому что в нем есть спецификация потенциально вызывающего исключения, тогда как в немB​::​f есть спецификация не вызывающего исключения. ]end example

Каждый раз, когда генерируется исключение и поиск handler ([except.handle]) встречает самый внешний блок функции со спецификацией исключения, не вызывающей выброса, функция std​::​terminate() вызывается. [ Note: Реализация не должна отклонять выражение только потому, что при выполнении оно вызывает или может вызвать исключение из функции со спецификацией не вызывающего исключения. ] [end noteExample:

extern void f();                // potentially-throwing

void g() noexcept {
  f();                          // valid, even if f throws
  throw 42;                     // valid, effectively a call to std​::​terminate
}

Вызов f правильно сформирован, хотя при его вызове f может возникнуть исключение. ]end example

Выражение e - это potentially-throwing если

  • e это function call чей postfix-expression имеет тип функции или тип указатель на функцию, с потенциально бросанием спецификации исключений, или

  • e неявно вызывает функцию (например, перегруженный оператор, функцию распределения в a new-expression, конструктор для аргумента функции или деструктор, если e это a full-expression), которая потенциально выбрасывает, или

  • e это throw-expression, или

  • e это dynamic_­cast выражение, которое приводится к ссылочному типу и требует проверки во время выполнения, или

  • e - это typeid выражение, применяемое к встроенному унарному * оператору (возможно заключенному в скобки), применяемому к указателю на тип полиморфного класса ([expr.typeid]), или

  • любой из immediate subexpressions из e потенциально метания.

Неявно объявленный конструктор для класса Xили конструктор без a, noexcept-specifier который по умолчанию используется в его первом объявлении, имеет спецификацию потенциально вызывающего исключения тогда и только тогда, когда любая из следующих конструкций потенциально выбрасывает:

  • конструктор, выбранный разрешением перегрузки в неявном определении конструктора для класса, X чтобы инициализировать потенциально созданный подобъект, или

  • подвыражение такой инициализации, такое как выражение аргумента по умолчанию, или,

  • для конструктора по умолчанию - инициализатор члена по умолчанию.

[ Note: Даже несмотря на то, что деструкторы для полностью сконструированных подобъектов вызываются при возникновении исключения во время выполнения конструктора ([except.ctor]), их спецификации исключений не вносят вклад в спецификацию исключения конструктора, потому что исключение, выброшенное из такого деструктора, std​::​terminate скорее вызовет чем экранировать конструктор ([except.throw], [except.terminate]). ]end note

Спецификация исключения для неявно объявленного деструктора или деструктора без a noexcept-specifierпотенциально может быть выброшена тогда и только тогда, когда любой из деструкторов для любого из его потенциально созданных подобъектов потенциально выбрасывает.

Спецификация исключения для неявно объявленного оператора присваивания или оператора присваивания без a, noexcept-specifier который используется по умолчанию в его первом объявлении, потенциально является бросанием тогда и только тогда, когда вызов любого оператора присваивания в неявном определении является потенциально бросающим.

A deallocation function без явного указания noexcept-specifier имеет спецификацию исключения, не вызывающего выброса.

[Example:

struct A {
  A(int = (A(5), 0)) noexcept;
  A(const A&) noexcept;
  A(A&&) noexcept;
  ~A();
};
struct B {
  B() throw();
  B(const B&) = default;        // implicit exception specification is noexcept(true)
  B(B&&, int = (throw Y(), 0)) noexcept;
  ~B() noexcept(false);
};
int n = 7;
struct D : public A, public B {
    int * p = new int[n];
    // D​::​D() potentially-throwing, as the new operator may throw bad_­alloc or bad_­array_­new_­length
    // D​::​D(const D&) non-throwing
    // D​::​D(D&&) potentially-throwing, as the default argument for B's constructor may throw
    // D​::​ D() potentially-throwing
};

Более того, если бы она A​::​~A() была виртуальной, программа была бы плохо сформирована, поскольку функция, которая переопределяет виртуальную функцию из базового класса, не должна иметь спецификации потенциально вызывающего исключения, если функция базового класса имеет спецификацию исключения, не вызывающего выброса. ]end example

Спецификация исключения считается, needed когда:

  • в выражении, то функция является единственным результатом поиска или выбранный элемент из набора перегруженных функций ([basic.lookup], [over.match], [over.over]);

  • функция есть odr-used или, если она появляется в неоцененном операнде, использовалась бы odr, если бы выражение было потенциально оцененным;

  • спецификация исключения сравнивается со спецификацией другого объявления (например, явная специализация или замещающая виртуальная функция);

  • функция определена; или

  • спецификация исключения необходима для специальной функции-члена по умолчанию, которая вызывает эту функцию. [ Note: Объявление по умолчанию не требует, чтобы спецификация исключения базовой функции-члена оценивалась до тех пор, пока не потребуется неявная спецификация исключения производной функции, но явное noexcept-specifierтребует неявной спецификации исключения для сравнения. ] end note

Спецификация исключения специальной функции-члена по умолчанию оценивается, как описано выше, только при необходимости; аналогично, noexcept-specifierспециализация шаблона функции или функция-член шаблона класса создается только при необходимости.