Difference between revisions of "cpp/language/try catch"
(huh, this never said unwinding for uncaught exceptions was implementation-defined) |
(p0012r1 addition to 15.3/3) |
||
Line 55: | Line 55: | ||
* {{tt|T}} is an unambiguous public base class of {{tt|E}} | * {{tt|T}} is an unambiguous public base class of {{tt|E}} | ||
* {{tt|T}} is a reference to an unambiguous public base class of {{tt|E}} | * {{tt|T}} is a reference to an unambiguous public base class of {{tt|E}} | ||
− | * {{tt|T}} is a (possibly cv-qualified) pointer {{rev inl|since=c++14|or a reference to a const pointer}}, and {{tt|E}} is also a pointer, which is implicitly convertible to T | + | * {{tt|T}} is a (possibly cv-qualified) pointer {{rev inl|since=c++14|or a reference to a const pointer}}, and {{tt|E}} is also a pointer, which is implicitly convertible to T by one or more of |
:* a standard {{rlp|implicit_cast|pointer conversion}} | :* a standard {{rlp|implicit_cast|pointer conversion}} | ||
:* a {{rlp|implicit_cast|qualification conversion}} | :* a {{rlp|implicit_cast|qualification conversion}} | ||
+ | {{rev begin}} | ||
+ | {{rev|since=c++17| | ||
+ | :* a {{rlp|implicit_cast|function pointer conversion}} | ||
+ | }} | ||
+ | {{rev end}} | ||
* {{tt|T}} is a pointer or a pointer to member {{rev inl|since=c++14|or a reference to a const pointer}}, while {{tt|E}} is {{lc|std::nullptr_t}}. | * {{tt|T}} is a pointer or a pointer to member {{rev inl|since=c++14|or a reference to a const pointer}}, while {{tt|E}} is {{lc|std::nullptr_t}}. | ||
Revision as of 10:12, 28 October 2015
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(C++11) | - | optional list 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 rvalue reference type, abstract class, incomplete type, 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 of type E
is thrown by any statement in compound-statement, it 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 a (possibly cv-qualified) pointer or a reference to a const pointer(since C++14), andE
is also a pointer, which is implicitly convertible to T by one or more of
- a standard pointer conversion
- a qualification conversion
(since C++17) |
-
T
is a pointer or a pointer to member or a reference to a const pointer(since C++14), whileE
is std::nullptr_t.
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(...) matched 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 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) |
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 matched by a pointer catch clause, because the exception object type is int, but throw nullptr; is 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
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"; } }
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'