close
Пространства имён
Варианты
Действия

Спецификация динамического исключения (до C++17)

Материал из cppreference.com
 
 
 
 

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

Синтаксис

throw(список-идентификаторов-типов (необязательно)) (1) (устарело в C++11)
(удалено в C++17)
1) Спецификация явного динамического исключения.
список-идентификаторов-типов разделённый запятыми список идентификаторов-типов, за идентификатором типа, представляющим расширение пакета, следует многоточие (...) (начиная с C++11)

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

void f() throw(int);            // OK: объявление функции
void (*pf)() throw (int);       // OK: объявление указателя на функцию
void g(void pfa() throw(int));  // OK: объявление параметра указателя на функцию
typedef int (*pf)() throw(int); // Ошибка: объявление typedef

Объяснение

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

Неполные типы, указатели или ссылки на неполные типы, отличные от cv void*, и ссылочные типы rvalue (начиная с C++11) не допускаются в спецификации исключений. Типы массивов и функций, если они используются, настраиваются на соответствующие типы указателей, cv-квалификация верхнего уровня также отбрасывается. Разрешены пакеты параметров (начиная с C++11).

Спецификация динамического исключения, чей набор скорректированных типов пуст (после того, как все пакеты расширены) (начиная с C++11), не является генерирующей исключение. Функция со спецификацией динамического исключения, не создающей исключения, не допускает никаких исключений.

Спецификация динамического исключения не считается частью типа функции.

Если функция генерирует исключение типа, не указанного в её спецификации исключения, вызывается функция std::unexpected. Функция по умолчанию вызывает std::terminate, но её можно заменить предоставленной пользователем функцией (через std::set_unexpected), которая может вызывать std::terminate или выдавать исключение. Если исключение, вызванное std::unexpected, принимается спецификацией исключения, раскручивание стека продолжается как обычно. Если это не так, и std::bad_exception разрешено спецификацией исключения, генерируется std::bad_exception. Иначе вызывается std::terminate.

Возможные исключения

Каждая функция f, указатель на функцию pf и указатель на функцию-элемент pmf имеют набор потенциальных исключений, который состоит из типов, которые могут быть брошены. Набор всех типов указывает, что может быть выдано любое исключение. Этот набор определяется следующим образом:

1) Если в объявлении f, pf или pmf используется спецификация динамического исключения, которая не допускает всех исключений (до C++11), набор состоит из типов, перечисленных в этой спецификации.
2) Иначе, если в объявлении f, pf или pmf используется noexcept(true), набор пуст.
(начиная с C++11)
3) Иначе набор является набором всех типов.

Примечание. Для неявно объявленных специальных функций-элементов (конструкторов, операторов присваивания и деструкторов) и для наследуемых конструкторов (начиная с C++11) набор потенциальных исключений представляет собой комбинацию наборов всех потенциальных исключений, которые они будут вызывать: конструкторы/операторы присваивания/деструкторы невариантных нестатических элементов данных, прямые базовые классы и, где это уместно, виртуальные базовые классы (включая выражения аргументов по умолчанию, как всегда).

Каждое выражение e имеет набор потенциальных исключений. Набор пуст, если e является основным константным выражением, иначе это объединение наборов потенциальных исключений всех непосредственных подвыражений e (включая выражения аргументов по умолчанию), в сочетании с другим набором, зависящим от формы e, следующим образом:

1) Если e является выражением вызова функции, пусть g обозначает вызываемую функцию, указатель на функцию или указатель на функцию-элемент, тогда
  • если в объявлении g используется спецификация динамического исключения, набор потенциальных исключений g добавляется к набору;
  • если в объявлении g используется noexcept(true), набор пуст;
(начиная с C++11)
  • иначе набор является набором всех типов.
2) Если e неявно вызывает функцию (это операторное выражение, и оператор перегружен, это выражение new и функция распределения памяти перегружена, или это полное выражение и вызывается деструктор временного объекта), то набор является набором этой функции.
3) Если e является выражением throw, то набор представляет собой исключение, которое будет инициализировано его операндом, или набор всех типов для повторного создания выражения throw (без операнда).
4) Если e является dynamic_cast ссылкой на полиморфный тип, набор состоит из std::bad_cast.
5) Если e является typeid, применённым к разыменованному указателю на полиморфный тип, набор состоит из std::bad_typeid.
6) Если e является выражением new с неконстантным размером массива, а выбранная функция распределения имеет непустой набор потенциальных исключений, набор состоит из std::bad_array_new_length.
(начиная с C++11)
void f() throw(int); // набор f() равен "int"
void g();            // набор g() это набор всех типов

struct A { A(); };                  // набор "new A" это набор всех типов
struct B { B() noexcept; };         // набор "B()" пуст
struct D() { D() throw (double); }; // набор new D это набор всех типов

Все неявно объявленные функции-элементы и наследуемые конструкторы (начиная с C++11)имеют спецификации исключений, выбранные следующим образом:

  • Если набор потенциальных исключений является набором всех типов, неявная спецификация исключения разрешает все исключения (спецификация исключения считается присутствующей, даже если она невыразима в коде и ведёт себя так, как будто спецификация исключения отсутствует) (до C++11)это noexcept(false) (начиная с C++11).
  • Иначе, если набор потенциальных исключений не пуст, в спецификации неявного исключения перечислены все типы из набора.
  • Иначе неявная спецификация исключения имеет вид throw() (до C++11)noexcept(true) (начиная с C++11).
struct A
{
    A(int = (A(5), 0)) noexcept;
    A(const A&) throw();
    A(A&&) throw();
    ~A() throw(X);
};

struct B
{
    B() throw();
    B(const B&) = default; // спецификация исключения "noexcept(true)"
    B(B&&, int = (throw Y(), 0)) noexcept;
    ~B() throw(Y);
};

int n = 7;
struct D : public A, public B
{
    // Может генерировать (исключение типа, которое соответствует обработчику типа)
    // std​::bad_array_new_length, но не генерировать исключение неправильного
    // распределения памяти
    (void*) new (std::nothrow) int[n];

    // D может иметь следующие неявно объявленные элементы:
    // D::D() throw(X, std::bad_array_new_length);
    // D::D(const D&) noexcept(true);
    // D::D(D&&) throw(Y);
    // D::~D() throw(X, Y);
};

Пример

Примечание: лучше компилировать в режиме C++98, чтобы избежать предупреждений. Несовместим с C++17 и более новыми версиями.

#include <cstdlib>
#include <exception>
#include <iostream>

class X {};
class Y {};
class Z : public X {};
class W {};

void f() throw(X, Y) 
{
    bool n = false;
    if (n) throw X(); // OK, вызовет std::terminate()
    if (n) throw Z(); // также OK
    throw W(); // вызовет std::unexpected()
}

void handler()
{
    std::cerr << "Это было неожиданно!\n"; // требуется сброс
    std::abort();
}

int main()
{
    std::set_unexpected(handler);
    f();
}

Вывод:

Это было неожиданно!

Отчёты о дефектах

Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:

Номер Применён Поведение в стандарте Корректное поведение
CWG 25 C++98 поведение присваивания и инициализации между указателями
на элементы с разными спецификациями исключений не указано
применено ограничение для указателей на
функции и ссылок
CWG 973 C++98 спецификация исключения может содержать типы функций, но
не указано соответствующее преобразование указателя на
функцию
определено
CWG 1267 C++11 ссылочные типы rvalue были разрешены в спецификациях
исключений
не разрешены
CWG 1351 C++98
C++11
аргумент по умолчанию (C++98) и инициализатор элемента по
умолчанию (C++11) игнорировались в неявной спецификации
исключений
не игнорируются
CWG 1777 C++11 спецификация throw(T...) не является спецификацией, не
допускающей выбрасывания исключения, даже если T
является пустым пакетом
не выбрасывает исключение, если пакет пуст
CWG 2191 C++98 набор потенциальных исключений выражения typeid может
содержать bad_typeid, даже если его нельзя сгенерировать
содержит bad_typeid, только если его можно
выбросить

Смотрите также

спецификатор noexcept(C++11) указывает, может ли функция генерировать исключения[править]