Difference between revisions of "cpp/language/try catch"
(Added the DR section.) |
(Added CWG issue #210 DR item.) |
||
Line 50: | Line 50: | ||
The formal parameter of the catch clause ({{spar|type-specifier-seq}} and {{spar|declarator}} or {{spar|type-specifier-seq}} and {{spar|abstract-declarator}}) determines which types of exceptions cause this catch clause to be entered. It cannot be an {{rlp|reference|rvalue reference type}}, {{rlp|abstract class}}, {{rlp|incomplete type}}, or pointer to incomplete type (except that pointers to (possibly {{rlp|cv}}-qualified) {{c|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 {{rlp|function|function declaration}}). | The formal parameter of the catch clause ({{spar|type-specifier-seq}} and {{spar|declarator}} or {{spar|type-specifier-seq}} and {{spar|abstract-declarator}}) determines which types of exceptions cause this catch clause to be entered. It cannot be an {{rlp|reference|rvalue reference type}}, {{rlp|abstract class}}, {{rlp|incomplete type}}, or pointer to incomplete type (except that pointers to (possibly {{rlp|cv}}-qualified) {{c|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 {{rlp|function|function declaration}}). | ||
− | When an exception | + | When an exception is thrown by any statement in {{spar|compound-statement}}, the {{rlp|throw#The exception object|exception object}} of type {{tt|E}} is matched against the types of the formal parameters {{tt|T}} of each catch-clause in {{spar|handler-seq}}, in the order in which the catch clauses are listed. The exception is a match if any of the following is true: |
* {{tt|E}} and {{tt|T}} are the same type (ignoring top-level cv-qualifiers on {{tt|T}}) | * {{tt|E}} and {{tt|T}} are the same type (ignoring top-level cv-qualifiers on {{tt|T}}) | ||
* {{tt|T}} is an lvalue-reference to (possibly cv-qualified) {{tt|E}} | * {{tt|T}} is an lvalue-reference to (possibly cv-qualified) {{tt|E}} | ||
Line 66: | Line 66: | ||
{{source| | {{source| | ||
− | try { | + | try |
+ | { | ||
f(); | 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 | ||
}} | }} | ||
Line 85: | Line 86: | ||
When entering a catch clause, if its formal parameter is a base class of the exception type, it is {{rlp|copy_initialization|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 {{rlp|copy elision}}). | When entering a catch clause, if its formal parameter is a base class of the exception type, it is {{rlp|copy_initialization|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 {{rlp|copy elision}}). | ||
{{source|1= | {{source|1= | ||
− | try { | + | 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 | // 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 | ||
} | } | ||
}} | }} | ||
Line 111: | Line 116: | ||
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. | 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. | ||
{{source|1= | {{source|1= | ||
− | try { | + | try |
+ | { | ||
f(); | f(); | ||
− | |||
− | |||
− | |||
− | |||
} | } | ||
+ | catch (const std::exception& e) | ||
+ | {} // will be executed if f() throws std::runtime_error | ||
+ | catch (const std::runtime_error& e) | ||
+ | {} // dead code! | ||
}} | }} | ||
Line 123: | Line 129: | ||
{{source|1= | {{source|1= | ||
label: | label: | ||
− | try { | + | try |
+ | { | ||
T1 t1; | T1 t1; | ||
− | try { | + | try |
+ | { | ||
T2 t2; | T2 t2; | ||
if(condition) | if(condition) | ||
goto label; // destroys t2, then destroys t1, then jumps to label | goto label; // destroys t2, then destroys t1, then jumps to label | ||
− | } catch (...) { | + | } |
− | } catch (...) { | + | catch (...) {} // catches the exception from the destructor of t2 |
+ | } | ||
+ | catch (...) {} // catches the exception from the destructor of t1 | ||
}} | }} | ||
Line 145: | Line 155: | ||
#include <vector> | #include <vector> | ||
− | int main() { | + | int main() |
− | try { | + | { |
+ | try | ||
+ | { | ||
std::cout << "Throwing an integer exception...\n"; | std::cout << "Throwing an integer exception...\n"; | ||
throw 42; | throw 42; | ||
− | } catch (int i) { | + | } |
+ | catch (int i) | ||
+ | { | ||
std::cout << " the integer exception was caught, with value: " << i << '\n'; | std::cout << " the integer exception was caught, with value: " << i << '\n'; | ||
} | } | ||
− | try { | + | try |
+ | { | ||
std::cout << "Creating a vector of size 5... \n"; | std::cout << "Creating a vector of size 5... \n"; | ||
std::vector<int> v(5); | std::vector<int> v(5); | ||
std::cout << "Accessing the 11th element of the vector...\n"; | std::cout << "Accessing the 11th element of the vector...\n"; | ||
std::cout << v.at(10); // vector::at() throws std::out_of_range | std::cout << v.at(10); // vector::at() throws std::out_of_range | ||
− | } catch (const std::exception& e) | + | } |
+ | catch (const std::exception& e) // caught by reference to base | ||
+ | { | ||
std::cout << " a standard exception was caught, with message '" | std::cout << " a standard exception was caught, with message '" | ||
<< e.what() << "'\n"; | << e.what() << "'\n"; | ||
} | } | ||
− | |||
} | } | ||
| p=true | output= | | p=true | output= | ||
Line 175: | Line 191: | ||
{{dr list begin}} | {{dr list begin}} | ||
{{dr list item|wg=cwg|dr=98|std=C++98|before=a switch statement can transfer control 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 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 end}} | {{dr list end}} | ||
{{langlinks|de|es|fr|it|ja|pt|ru|zh}} | {{langlinks|de|es|fr|it|ja|pt|ru|zh}} |
Revision as of 23:08, 17 January 2022
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 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 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
orconst U&
(since C++14), andU
is a pointer or pointer to member(since C++17) type, andE
is also a pointer or pointer to member(since C++17) 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) |
-
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 (...) 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
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 |