Спецификация динамического исключения (до C++17)
Перечисляет исключения, которые прямо или косвенно может генерировать функция.
Содержание |
[править]Синтаксис
throw( список-идентификаторов-типов (необязательно)) | (1) | (устарело в C++11) (удалено в C++17) | |||||||
список-идентификаторов-типов | — | разделённый запятыми список идентификаторов-типов, за идентификатором типа, представляющим расширение пакета, следует многоточие (...)(начиная с C++11) |
Явная спецификация динамического исключения должна появляться только в деклараторе функции для типа функции, указателя на тип функции, ссылке на тип функции или указателя на тип функции-элемента, который является типом верхнего уровня объявления или определения, или для такого типа, появляющегося как параметр или тип возвращаемого значения в деклараторе функции.
void f()throw(int);// OK: объявление функцииvoid(*pf)()throw(int);// OK: объявление указателя на функциюvoid g(void pfa()throw(int));// OK: объявление параметра указателя на функциюtypedefint(*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
имеют набор потенциальных исключений, который состоит из типов, которые могут быть брошены. Набор всех типов указывает, что может быть выдано любое исключение. Этот набор определяется следующим образом:
f
, pf
или pmf
используется спецификация динамического исключения, которая не допускает всех исключений(до C++11), набор состоит из типов, перечисленных в этой спецификации.(начиная с C++11) |
Примечание. Для неявно объявленных специальных функций-элементов (конструкторов, операторов присваивания и деструкторов) и для наследуемых конструкторов(начиная с C++11) набор потенциальных исключений представляет собой комбинацию наборов всех потенциальных исключений, которые они будут вызывать: конструкторы/операторы присваивания/деструкторы невариантных нестатических элементов данных, прямые базовые классы и, где это уместно, виртуальные базовые классы (включая выражения аргументов по умолчанию, как всегда).
Каждое выражение e
имеет набор потенциальных исключений. Набор пуст, если e
является основным константным выражением, иначе это объединение наборов потенциальных исключений всех непосредственных подвыражений e
(включая выражения аргументов по умолчанию), в сочетании с другим набором, зависящим от формы e
, следующим образом:
e
является выражением вызова функции, пусть g
обозначает вызываемую функцию, указатель на функцию или указатель на функцию-элемент, тогда - если в объявлении
g
используется спецификация динамического исключения, набор потенциальных исключенийg
добавляется к набору;
- если в объявлении
| (начиная с C++11) |
- иначе набор является набором всех типов.
e
неявно вызывает функцию (это операторное выражение, и оператор перегружен, это выражение new и функция распределения памяти перегружена, или это полное выражение и вызывается деструктор временного объекта), то набор является набором этой функции.e
является выражением throw, то набор представляет собой исключение, которое будет инициализировано его операндом, или набор всех типов для повторного создания выражения throw (без операнда).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();// также OKthrow 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) | указывает, может ли функция генерировать исключения |