Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/language/try catch"

From cppreference.com
< cpp‎ | language
(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 of type {{tt|E}} is thrown by any statement in {{spar|compound-statement}}, it 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:
+
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
 
 
}
 
}
 +
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::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
+
catch (const std::exception& e) // reference to the base of a polymorphic object
    std::cout << e.what(); // information from length_error printed
+
{
 +
    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!
 
 
}
 
}
 +
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 (...) { } // catches the exception from the destructor of t2
+
         }
     } catch (...) { } // catches the exception from the destructor of t1
+
        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) { // caught by reference to base
+
     }
 +
    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

 
 
C++ language
General topics
Flow control
Conditional execution statements
if
Iteration statements (loops)
for
range-for (C++11)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications (until C++17*)
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
explicit (C++11)
static

Special member functions
Templates
Miscellaneous
 
 

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
1) Catch-clause that declares a named formal parameter
try { /* */ } catch (const std::exception& e) { /* */ }
2) Catch-clause that declares an unnamed parameter
try { /* */ } catch (const std::exception&) { /* */ }
3) Catch-all handler, which is activated for any 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 and T are the same type (ignoring top-level cv-qualifiers on T)
  • T is an lvalue-reference to (possibly cv-qualified) E
  • T is an unambiguous public base class of E
  • T is a reference to an unambiguous public base class of E
  • T is (possibly cv-qualified) U or const U&(since C++14), and U is a pointer or pointer to member(since C++17) type, and E is also a pointer or pointer to member(since C++17) type that is implicitly convertible to U by one or more of
(since C++17)
  • T is a pointer or a pointer to member or a reference to a const pointer(since C++14), while E 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

try, catch, throw

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