Handling exceptions
Исключение exception может быть обработано обработчиком.
Содержание |
Обработчик
catch
(
attr
(необязательно)
type-specifier-seq
declarator
)
compound-statement
|
(1) | ||||||||
catch
(
attr
(необязательно)
type-specifier-seq
abstract-declarator
(необязательно)
)
compound-statement
|
(2) | ||||||||
catch
(
...
)
compound-statement
|
(3) | ||||||||
| attr | - | (since C++11) любое количество attributes , применяется к параметру |
| type-specifier-seq | - | часть объявления формального параметра, такая же как в parameter list функции |
| declarator | - | часть объявления параметра, такая же как в parameter list функции |
| abstract-declarator | - | часть объявления безымянного параметра, такая же как в parameter list функции |
| compound-statement | - | составной оператор compound statement |
Объявление параметра в обработчике описывает тип(ы) исключений, которые могут привести к входу в этот обработчик.
Если параметр объявлен с одним из следующих типов, программа является некорректной:
- тип неполного типа
- тип абстрактного класса
|
(начиная с C++11) |
- указатель на неполный тип, отличный от (возможно, cv-квалифицированного) void
- lvalue-ссылка на неполный тип
Если параметр объявлен как имеющий тип «массив
T
» или тип функции
T
, тип корректируется до «указатель на
T
».
Обработчик с типом параметра
T
может быть сокращенно назван «обработчиком типа
T
».
Сопоставление исключений
Каждый try блок ассоциируется с несколькими обработчиками, эти обработчики формируют последовательность обработчиков. Когда исключение выбрасывается из try блока, обработчики в последовательности проверяются в порядке их следования для сопоставления с исключением.
Обработчик соответствует
объекту исключения
типа
E
при выполнении любого из следующих условий:
-
Обработчик имеет тип «возможно cv-квалифицированный
T» или «lvalue-ссылка на возможно cv-квалифицированныйT», и удовлетворяется любое из следующих условий:
-
-
EиTявляются одним и тем же типом (игнорируя cv-квалификаторы верхнего уровня). -
Tявляется однозначным публичным базовым классом дляE.
-
-
Обработчик имеет тип «возможно cv-квалифицированный
T» или const T & , гдеTявляется типом указателя или указателя на член, и удовлетворяется любое из следующих условий:
-
-
E— это указатель или указатель на член, который может быть преобразован вTпо крайней мере одним из следующих преобразований:
-
- Стандартное преобразование указателей , не включающее преобразования в указатели на private, protected или неоднозначные классы.
-
|
(начиная с C++17) |
|
(начиная с C++11) |
Обработчик catch ( ... ) перехватывает исключения любого типа. Если он присутствует, он может быть только последним обработчиком в последовательности обработчиков. Этот обработчик может использоваться для гарантии, что никакие неперехваченные исключения не могут покинуть функцию, которая предоставляет nothrow exception guarantee .
try { f(); } catch (const std::overflow_error& e) {} // этот блок выполняется, если f() выбрасывает std::overflow_error (правило соответствия типов) catch (const std::runtime_error& e) {} // этот блок выполняется, если f() выбрасывает std::underflow_error (правило базового класса) catch (const std::exception& e) {} // этот блок выполняется, если f() выбрасывает std::logic_error (правило базового класса) catch (...) {} // этот блок выполняется, если f() выбрасывает std::string или int или любой другой несвязанный тип
Если среди обработчиков для блока try не найдено соответствия, поиск подходящего обработчика продолжается в динамически окружающем блоке try того же потока (начиная с C++11) .
Если соответствующий обработчик не найден, std::terminate вызывается; производится ли раскрутка стека перед вызовом std::terminate — определяется реализацией.
Обработка исключений
При выбросе исключения управление передается ближайшему обработчику с соответствующим типом; «ближайший» означает обработчик, для которого составной оператор или список инициализации членов (если присутствует) следующий за ключевым словом try был наиболее недавно введен потоком управления и еще не был завершен.
Инициализация параметра обработчика
Параметр, объявленный в списке параметров (если есть), типа «возможно cv-квалифицированный
T
» или «ссылка на lvalue на возможно cv-квалифицированный
T
», инициализируется из
объекта исключения
, типа
E
, следующим образом:
-
Если
Tявляется базовым классом дляE, параметр копически инициализируется из lvalue типаT, указывающего на соответствующий базовый подобъект объекта исключения. -
В противном случае параметр копически инициализируется из lvalue типа
E, указывающего на объект исключения.
Время жизни параметра завершается при выходе из обработчика, после уничтожения любых объектов с автоматической storage duration инициализированных в пределах обработчика.
Когда параметр объявлен как объект, любые изменения этого объекта не повлияют на объект исключения.
Когда параметр объявлен как ссылка на объект, любые изменения в ссылаемом объекте являются изменениями объекта исключения и вступят в силу, если это исключение будет переброшено.
Активация обработчика
Обработчик считается активным когда инициализация параметра обработчика (если таковой имеется) завершена.
Кроме того, неявный обработчик считается активным, когда std::terminate вызывается из-за операции throw.
Обработчик больше не считается активным, когда обработчик завершает работу.
Исключение с самым последним активированным обработчиком, который все еще активен, называется текущим обрабатываемым исключением . Такое исключение может быть переброшено .
Управление потоком выполнения
compound-statement обработчика является оператором с ограниченным потоком управления :
void f() { goto label; // ошибка try { goto label; // ошибка } catch (...) { goto label: // OK label: ; } }
Примечания
Stack unwinding происходит во время передачи управления обработчику. Когда обработчик становится активным, раскрутка стека уже завершена.
Исключение, выброшенное выражением throw throw 0 не соответствует обработчику типа указателя или указателя на член.
|
(since C++11) |
Объекты исключений никогда не могут иметь типы массивов или функций, поэтому обработчик ссылки на тип массива или функции никогда не соответствует ни одному объекту исключения.
Можно написать обработчики, которые никогда не будут выполнены, например, разместив обработчик для конечного производного класса после обработчика для соответствующего однозначного открытого базового класса:
try { f(); } catch (const std::exception& e) {} // будет выполнено, если f() выбрасывает std::runtime_error catch (const std::runtime_error& e) {} // недостижимый код!
Многие реализации чрезмерно расширяют разрешение CWG issue 388 на обработчики ссылок на типы неконстантных указателей:
int i; try { try { throw static_cast<float*>(nullptr); } catch (void*& pv) { pv = &i; throw; } } catch (const float* pf) { assert(pf == nullptr); // должно выполняться, но не выполняется в MSVC и Clang }
Ключевые слова
Пример
Следующий пример демонстрирует несколько случаев использования обработчиков:
#include <iostream> #include <vector> int main() { try { std::cout << "Throwing an integer exception...\n"; throw 42; } catch (int i) { std::cout << " the integer exception was caught, with value: " << i << '\n'; } try { std::cout << "Creating a vector of size 5... \n"; std::vector<int> v(5); std::cout << "Accessing the 11th element of the vector...\n"; std::cout << v.at(10); // vector::at() throws std::out_of_range } catch (const std::exception& e) // caught by reference to base { std::cout << " a standard exception was caught, with message: '" << e.what() << "'\n"; } }
Возможный вывод:
Throwing an integer exception... the integer exception was caught, with value: 42 Creating a vector of size 5... Accessing the 11th element of the vector... a standard exception was caught, with message: 'out_of_range'
Отчеты о дефектах
Следующие отчеты об изменениях поведения, влияющие на дефекты, были применены задним числом к ранее опубликованным стандартам C++.
| DR | Applied to | Behavior as published | Correct behavior |
|---|---|---|---|
| CWG 98 | C++98 | a switch statement can transfer control into a handler | prohibited |
| CWG 210 | C++98 | throw expressions were matched against the handlers |
exception objects are
matched against the handlers |
| CWG 388 | C++98 |
an exception of pointer or pointer to member type could
not be matched by a const reference to a different type |
made matchable
when convertible |
| CWG 1166 | C++98 |
the behavior was unspecified when a handler whose
type is a reference to an abstract class type is matched |
abstract class types are
not allowed for handlers |
| CWG 1769 | C++98 |
when the type of the handler is a base of the type of
the exception object, a converting constructor might be used for the initialization of the handler parameter |
the parameter is copy-initialized
from the corresponding base class subobject of the exception object |
| CWG 2093 | C++98 |
an exception object of pointer to object type could not match a
handler of pointer to object type through qualification conversion |
allowed |
Ссылки
- Стандарт C++23 (ISO/IEC 14882:2024):
-
- 14.4 Обработка исключения [except.handle]
- Стандарт C++20 (ISO/IEC 14882:2020):
-
- 14.4 Обработка исключения [except.handle]
- Стандарт C++17 (ISO/IEC 14882:2017):
-
- 18.3 Обработка исключения [except.handle]
- Стандарт C++14 (ISO/IEC 14882:2014):
-
- 15.3 Обработка исключения [except.handle]
- Стандарт C++11 (ISO/IEC 14882:2011):
-
- 15.3 Обработка исключения [except.handle]
- Стандарт C++03 (ISO/IEC 14882:2003):
-
- 15.3 Обработка исключения [except.handle]
- Стандарт C++98 (ISO/IEC 14882:1998):
-
- 15.3 Обработка исключения [except.handle]