Difference between revisions of "cpp/language/try catch"
m (→Notes: correct pass/fail) |
m (→Defect reports) |
||
Line 209: | Line 209: | ||
{{dr list item|wg=cwg|dr=98|std=C++98|before=a switch statement can transfer control<br>into a try block or into a handler|after=prohibited}} | {{dr list item|wg=cwg|dr=98|std=C++98|before=a switch statement can transfer control<br>into a try block or into a handler|after=prohibited}} | ||
{{dr list item|wg=cwg|dr=210|std=C++98|before=the throw expression was matched against the catch clauses|after=the exception object is matched<br>against the catch clauses}} | {{dr list item|wg=cwg|dr=210|std=C++98|before=the throw expression was matched against the catch clauses|after=the exception object is matched<br>against the catch clauses}} | ||
− | {{dr list item|wg=cwg|dr=388|std=C++98|before=an exception of pointer or pointer to member type could not<br>be matched by a const reference to a difference type|after=made matchable}} | + | {{dr list item|wg=cwg|dr=388|std=C++98|before=an exception of pointer or pointer to member type could not<br>be matched by a const reference to a difference type|after=made matchable when convertible}} |
{{dr list item|wg=cwg|dr=1166|std=C++98|before=the behavior was unspecified when a catch clause whose<br>exception type is a reference to an abstract class type is matched|after=abstract class types are not<br>allowed for catch clauses}} | {{dr list item|wg=cwg|dr=1166|std=C++98|before=the behavior was unspecified when a catch clause whose<br>exception type is a reference to an abstract class type is matched|after=abstract class types are not<br>allowed for catch clauses}} | ||
{{dr list item|wg=cwg|dr=1769|std=C++98|before=when the type of the exception declared in the catch-clause is a<br>base of the type of the exception object, a converting constructor<br>might be used for the initialization of the catch-clause parameter|after=the parameter is copy-initialized<br>from the corresponding base class<br>subobject of the exception object}} | {{dr list item|wg=cwg|dr=1769|std=C++98|before=when the type of the exception declared in the catch-clause is a<br>base of the type of the exception object, a converting constructor<br>might be used for the initialization of the catch-clause parameter|after=the parameter is copy-initialized<br>from the corresponding base class<br>subobject of the exception object}} |
Revision as of 21:57, 19 November 2023
Associates one or more exception handlers (catch-clauses) with a compound statement.
Contents |
Syntax
try compound-statement handler-sequence
|
|||||||||
where handler-sequence is a sequence of one or more handlers, which have the following syntax:
catch ( attr (optional) type-specifier-seq declarator ) compound-statement
|
(1) | ||||||||
catch ( attr (optional) type-specifier-seq abstract-declarator (optional) ) compound-statement
|
(2) | ||||||||
catch ( ... ) compound-statement
|
(3) | ||||||||
compound-statement | - | brace-enclosed sequence of statements |
attr | - | (since C++11) any number of attributes, applies to the formal parameter |
type-specifier-seq | - | part of a formal parameter declaration, same as in a function parameter list |
declarator | - | part of a formal parameter declaration, same as in a function parameter list |
abstract-declarator | - | part of an unnamed formal parameter declaration, same as in function parameter list |
try { /* */ } catch (const std::exception& e) { /* */ }
try { /* */ } catch (const std::exception&) { /* */ }
try { /* */ } catch (...) { /* */ }
Explanation
- See throw exceptions for more information about throw-expressions
A try-block is a statement, and as such, can appear anywhere a statement can appear (that is, as one of the statements in a compound statement, including the function body compound statement). See function-try-block for the try blocks around function bodies. The following description applies to both try-blocks and function-try-blocks.
The formal parameter of the catch clause (type-specifier-seq and declarator or type-specifier-seq and abstract-declarator) determines which types of exceptions cause this catch clause to be entered. It cannot be an incomplete type, abstract class type, rvalue reference type,(since C++11) or pointer to incomplete type (except that pointers to (possibly cv-qualified) void are allowed). If the type of the formal parameter is array type or function type, it is treated as the corresponding pointer type (similar to a function declaration).
When an exception is thrown by any statement in compound-statement, the exception object of type E
is matched against the types of the formal parameters T
of each catch-clause in handler-seq, in the order in which the catch clauses are listed. The exception is a match if any of the following is true:
-
E
andT
are the same type (ignoring top-level cv-qualifiers onT
) -
T
is an lvalue-reference to (possibly cv-qualified)E
-
T
is an unambiguous public base class ofE
-
T
is a reference to an unambiguous public base class ofE
-
T
is (possibly cv-qualified)U
or const U&, andU
is a pointer or pointer to member type, andE
is also a pointer or pointer to member type that is implicitly convertible toU
by one or more of
- a standard pointer conversion other than one to a private, protected, or ambiguous base class
- a qualification conversion
(since C++17) |
|
(since C++11) |
try { f(); } catch (const std::overflow_error& e) {} // this executes if f() throws std::overflow_error (same type rule) catch (const std::runtime_error& e) {} // this executes if f() throws std::underflow_error (base class rule) catch (const std::exception& e) {} // this executes if f() throws std::logic_error (base class rule) catch (...) {} // this executes if f() throws std::string or int or any other unrelated type
The catch-all clause catch (...) matches exceptions of any type. If present, it has to be the last catch clause in the handler-seq. Catch-all block may be used to ensure that no uncaught exceptions can possibly escape from a function that offers nothrow exception guarantee.
If no matches are found after all catch-clauses were examined, the exception propagation continues to the containing try-block, as described in throw-expression. If there are no containing try-blocks left, std::terminate is executed (in this case, it is implementation-defined whether any stack unwinding occurs at all: throwing an uncaught exception is permitted to terminate the program without invoking any destructors).
When entering a catch clause, if its formal parameter is a base class of the exception type, it is copy-initialized from the base class subobject of the exception object. Otherwise, it is copy-initialized from the exception object (this copy is subject to copy elision).
try { std::string("abc").substr(10); // throws std::length_error } // catch (std::exception e) // copy-initialization from the std::exception base // { // std::cout << e.what(); // information from length_error is lost // } catch (const std::exception& e) // reference to the base of a polymorphic object { std::cout << e.what(); // information from length_error printed }
If the parameter of the catch-clause is a reference type, any changes made to it are reflected in the exception object, and can be observed by another handler if the exception is rethrown with throw;. If the parameter is not a reference, any changes made to it are local and its lifetime ends when the handler exits.
Within a catch-clause, std::current_exception can be used to capture the exception in an std::exception_ptr, and std::throw_with_nested may be used to build nested exceptions. |
(since C++11) |
A goto or switch statement shall not be used to transfer control into a try block or into a handler.
Other than by throwing or rethrowing the exception, the catch-clause after a regular try block (not function-try-block) may be exited with a return, continue, break, goto, or by reaching the end of its compound-statement. In any case, this destroys the exception object (unless an instance of std::exception_ptr exists that refers to it).
Notes
The throw-expression throw NULL; is not guaranteed to be matched by a pointer catch clause, because the exception object type may be int, but throw nullptr; is assuredly matched by any pointer or pointer-to-member catch clause.
If a catch-clause for a derived class is placed after the catch-clause for a base class, the derived catch-clause will never be executed:
try { f(); } catch (const std::exception& e) {} // will be executed if f() throws std::runtime_error catch (const std::runtime_error& e) {} // dead code!
If goto is used to exit a try-block and if any of the destructors of block-scoped automatic variables that are executed by the goto
throw exceptions, those exceptions are caught by the try blocks in which the variables are defined:
label: try { T1 t1; try { T2 t2; if (condition) goto label; // destroys t2, then destroys t1, then jumps to label } catch (...) {} // catches the exception from the destructor of t2 } catch (...) {} // catches the exception from the destructor of t1
Many implementations overly extend the resolution of CWG388 to handlers of reference of a non-const pointer types:
int i; try { try { throw static_cast<float*>(nullptr); } catch (void*& pv) { pv = &i; throw; } } catch (const float* pf) { assert(pf == nullptr); // should pass, but fails on mainstream implementations }
Keywords
Example
The following example demonstrates several usage cases of the try-catch
block
#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"; } }
Possible output:
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'
Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
CWG 98 | C++98 | a switch statement can transfer control into a try block or into a handler |
prohibited |
CWG 210 | C++98 | the throw expression was matched against the catch clauses | the exception object is matched against the catch clauses |
CWG 388 | C++98 | an exception of pointer or pointer to member type could not be matched by a const reference to a difference type |
made matchable when convertible |
CWG 1166 | C++98 | the behavior was unspecified when a catch clause whose exception type is a reference to an abstract class type is matched |
abstract class types are not allowed for catch clauses |
CWG 1769 | C++98 | when the type of the exception declared in the catch-clause is a base of the type of the exception object, a converting constructor might be used for the initialization of the catch-clause 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 |