Dynamic exception specification (until C++17)
Перечисляет исключения, которые функция может выбрасывать прямо или косвенно.
Содержание |
Синтаксис
throw(
type-id-list
(optional)
)
|
(1) |
(deprecated in C++11)
(removed in C++17) |
|||||||
| type-id-list | - | список, разделённый запятыми, из type-ids , type-id, представляющий pack expansion , сопровождается многоточием (...) (since 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 .
Инстанцирование
Динамическая спецификация исключений специализации шаблона функции не инстанцируется вместе с объявлением функции; она инстанцируется только тогда, когда требуется (как определено ниже).
Динамическая спецификация исключений неявно объявленной специальной функции-члена также вычисляется только когда это необходимо (в частности, неявное объявление функции-члена производного класса не требует инстанцирования спецификации исключений базовой функции-члена).
Когда динамическая спецификация исключений специализации шаблона функции требуется , но еще не была инстанцирована, зависимые имена подвергаются поиску, и любые шаблоны, используемые в expression , инстанцируются как если бы для объявления специализации.
Динамическая спецификация исключений функции считается необходимой в следующих контекстах:
- в выражении, где функция выбирается с помощью разрешения перегрузки
- функция является odr-used
- функция была бы odr-used, но появляется в невычисляемом операнде
template<class T> T f() throw(std::array<char, sizeof(T)>); int main() { decltype(f<void>()) *p; // f не вычисляется, но требуется спецификация исключений // ошибка, потому что инстанцирование спецификации исключений // вычисляет sizeof(void) }
- спецификация необходима для сравнения с другим объявлением функции (например, при переопределении виртуальной функции или явной специализации шаблона функции)
- в определении функции
- спецификация необходима, поскольку специальная функция-член по умолчанию должна проверить её, чтобы определить свою собственную спецификацию исключений (это происходит только тогда, когда спецификация специальной функции-члена по умолчанию сама по себе необходима).
Потенциальные исключения
Каждая функция
f
, указатель на функцию
pf
и указатель на функцию-член
pmf
имеет
набор потенциальных исключений
, который состоит из типов, которые могут быть выброшены. Набор всех типов указывает, что может быть выброшено любое исключение. Этот набор определяется следующим образом:
f
,
pf
или
pmf
использует динамическую спецификацию исключений
которая не разрешает все исключения
(до C++11)
, множество состоит из типов, перечисленных в этой спецификации.
|
2)
В противном случае, если объявление
f
,
pf
, или
pmf
использует
noexcept(true)
, множество является пустым.
|
(since C++11) |
Примечание: для неявно объявленных специальных функций-членов (конструкторов, операторов присваивания и деструкторов) и для наследуемых конструкторов (since C++11) , набор потенциальных исключений представляет собой комбинацию наборов потенциальных исключений всего, что они вызывают: конструкторов/операторов присваивания/деструкторов невариантных нестатических членов данных, прямых базовых классов и, где это уместно, виртуальных базовых классов (включая выражения аргументов по умолчанию, как всегда).
Каждое выражение
e
имеет
набор потенциальных исключений
. Набор пуст, если
e
является
core constant expression
, в противном случае это объединение наборов потенциальных исключений всех непосредственных подвыражений
e
(включая
default argument expressions
), объединенное с другим набором, который зависит от формы
e
, как описано далее:
e
является выражением вызова функции, пусть
g
обозначает функцию, указатель на функцию или указатель на функцию-член, который вызывается, тогда
-
-
если объявление
gиспользует динамическую спецификацию исключений, набор потенциальных исключенийgдобавляется к множеству;
-
если объявление
|
(начиная с C++11) |
-
- иначе, множество состоит из всех типов.
e
неявно вызывает функцию (это выражение-оператор и оператор перегружен, это
new-expression
и функция выделения памяти перегружена, или это полное выражение и вызывается деструктор временного объекта), то множество является множеством этой функции.
e
является
throw-expression
, множество представляет собой исключение, которое было бы инициализировано его операндом, или множество всех типов для повторно выбрасываемого throw-expression (без операнда).
e
является
typeid
применённым к разыменованному указателю на полиморфный тип, множество состоит из
std::bad_typeid
.
|
6)
Если
e
является
new-expression
с неконстантным размером массива, и выбранная функция выделения памяти имеет непустой набор потенциальных исключений, то этот набор состоит из
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 - множество всех типов
Все неявно объявленные функции-члены и наследуемые конструкторы (since 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); };
Примечания
Clang считает, что правило инстанциации спецификации динамических исключений изменено в C++11 согласно CWG1330 , см. LLVM #56349 .
Ключевые слова
Пример
Примечание: рекомендуется компилировать в режиме 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, would call std::terminate() if (n) throw Z(); // also OK throw W(); // will call std::unexpected() } void handler() { std::cerr << "That was unexpected!\n"; // flush needed std::abort(); } int main() { std::set_unexpected(handler); f(); }
Вывод:
That was unexpected!
Отчеты о дефектах
Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C++.
| DR | Applied to | Behavior as published | Correct behavior |
|---|---|---|---|
| CWG 25 | C++98 |
поведение присваивания и инициализации
между указателями на члены с различными спецификациями исключений было не определено |
применить ограничение
для указателей на функции и ссылок |
| CWG 973 | C++98 |
спецификация исключений может содержать типы функций, но
соответствующее преобразование указателей на функции не было определено |
определено |
| CWG 1330 | 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
specifier
(C++11)
|
определяет, может ли функция генерировать исключения |