Спецификация динамического исключения (до C++17) — cppreference.com
Перечисляет исключения, которые прямо или косвенно может генерировать функция.
Синтаксис
throw(список-идентификаторов-типов (необязательно))
|
(1) | (устарело в C++11) (удалено в C++17) | |||||||
1) Спецификация явного динамического исключения.
Явная спецификация динамического исключения должна появляться только в деклараторе функции для типа функции, указателя на тип функции, ссылке на тип функции или указателя на тип функции-элемента, который является типом верхнего уровня объявления или определения, или для такого типа, появляющегося как параметр или тип возвращаемого значения в деклараторе функции.
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) Иначе, если в объявлении |
(начиная с C++11) |
3) Иначе набор является набором всех типов.
Примечание. Для неявно объявленных специальных функций-элементов (конструкторов, операторов присваивания и деструкторов) и для наследуемых конструкторов (начиная с C++11) набор потенциальных исключений представляет собой комбинацию наборов всех потенциальных исключений, которые они будут вызывать: конструкторы/операторы присваивания/деструкторы невариантных нестатических элементов данных, прямые базовые классы и, где это уместно, виртуальные базовые классы (включая выражения аргументов по умолчанию, как всегда).
Каждое выражение e имеет набор потенциальных исключений. Набор пуст, если e является основным константным выражением, иначе это объединение наборов потенциальных исключений всех непосредственных подвыражений e (включая выражения аргументов по умолчанию), в сочетании с другим набором, зависящим от формы e, следующим образом:
1) Если e является выражением вызова функции, пусть g обозначает вызываемую функцию, указатель на функцию или указатель на функцию-элемент, тогда
- если в объявлении
gиспользуется спецификация динамического исключения, набор потенциальных исключенийgдобавляется к набору;
- если в объявлении
|
(начиная с C++11) |
- иначе набор является набором всех типов.
2) Если e неявно вызывает функцию (это операторное выражение, и оператор перегружен, это выражение new и функция распределения памяти перегружена, или это полное выражение и вызывается деструктор временного объекта), то набор является набором этой функции.
3) Если e является выражением throw, то набор представляет собой исключение, которое будет инициализировано его операндом, или набор всех типов для повторного создания выражения throw (без операнда).
4) Если e является dynamic_cast ссылкой на полиморфный тип, набор состоит из std::bad_cast.
5) Если e является typeid, применённым к разыменованному указателю на полиморфный тип, набор состоит из std::bad_typeid.
|
6) Если |
(начиная с 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, только если его можновыбросить |