[except] (original) (raw)

14 Exception handling [except]

14.1 Preamble [except.pre]

Exception handling provides a way of transferring control and information from a point in the execution of a thread to an exception handler associated with a point previously passed by the execution.

A handler will be invoked only by throwing an exception in code executed in the handler's try block or in functions called from the handler's try block.

[Note 1:

Within this Clause “try block” is taken to mean both try-block andfunction-try-block.

— _end note_]

[Example 1: void f() { goto l1; goto l2; try { goto l1; goto l2; l1: ;} catch (...) { l2: ;goto l1; goto l2; } } — _end example_]

Agoto,break,return, orcontinuestatement can be used to transfer control out of a try block or handler.

When this happens, each variable declared in the try block will be destroyed in the context that directly contains its declaration.

[Example 2: lab: try { T1 t1;try { T2 t2;if (condition) goto lab;} catch(...) { } } catch(...) { }

Here, executinggoto lab;will destroy firstt2, thent1, assuming theconditiondoes not declare a variable.

Any exception thrown while destroyingt2will result in executinghandler 2; any exception thrown while destroyingt1will result in executinghandler 1.

— _end example_]

An exception thrown during the execution of thecompound-statementor, for constructors and destructors, during the initialization or destruction, respectively, of the class's subobjects, transfers control to a handler in afunction-try-blockin the same way as an exception thrown during the execution of atry-blocktransfers control to other handlers.

[Example 3: int f(int);class C { int i;double d;public: C(int, double);}; C::C(int ii, double id) try : i(f(ii)), d(id) { } catch (...) { } — _end example_]

14.2 Throwing an exception [except.throw]

Throwing an exception transfers control to a handler.

An object is passed and the type of that object determines which handlers can catch it.

[Example 1:

throw "Help!";can be caught by ahandlerofconst char*type:try { } catch(const char* p) { } andclass Overflow { public: Overflow(char,double,double);};void f(double x) { throw Overflow('+',x,3.45e107);} can be caught by a handler for exceptions of typeOverflow:try { f(1.2);} catch(Overflow& oo) { }

— _end example_]

When an exception is thrown, control is transferred to the nearest handler with a matching type ([except.handle]); “nearest” means the handler for which thecompound-statement orctor-initializerfollowing thetrykeyword was most recently entered by the thread of control and not yet exited.

Throwing an exception initializes an object with dynamic storage duration, called theexception object.

If the type of the exception object would be an incomplete type ([basic.types.general]), an abstract class type ([class.abstract]), or a pointer to an incomplete type other thancv void ([basic.compound]), the program is ill-formed.

The memory for the exception object is allocated in an unspecified way, except as noted in [basic.stc.dynamic.allocation].

If a handler exits by rethrowing, control is passed to another handler for the same exception object.

The points of potential destruction for the exception object are:

Among all points of potential destruction for the exception object, there is an unspecified last one where the exception object is destroyed.

[Note 2:

No other thread synchronization is implied in exception handling.

— _end note_]

The implementation may then deallocate the memory for the exception object; any such deallocation is done in an unspecified way.

[Note 3:

A thrown exception does not propagate to other threads unless caught, stored, and rethrown using appropriate library functions; see [propagation] and [futures].

— _end note_]

Let T denote the type of the exception object.

Copy-initialization of an object of type T from an lvalue of type const T in a context unrelated to Tshall be well-formed.

If T is a class type, the selected constructor is odr-used ([basic.def.odr]) and the destructor of T is potentially invoked ([class.dtor]).

An exception is considered uncaughtafter completing the initialization of the exception object until completing the activation of a handler for the exception ([except.handle]).

[Note 4:

As a consequence, an exception is considered uncaught during any stack unwinding resulting from it being thrown.

— _end note_]

If an exception is rethrown ([expr.throw], [propagation]), it is considered uncaught from the point of rethrow until the rethrown exception is caught.

[Note 5:

The function std​::​uncaught_exceptions ([uncaught.exceptions]) returns the number of uncaught exceptions in the current thread.

— _end note_]

An exception is considered caught when a handler for that exception becomes active.

[Note 6:

An exception can have active handlers and still be considered uncaught if it is rethrown.

— _end note_]

If the exception handling mechanism handling an uncaught exception directly invokes a function that exits via an exception, the function std​::​terminate is invoked.

[Example 2: struct C { C() { } C(const C&) { if (std::uncaught_exceptions()) { throw 0; } } };int main() { try { throw C(); } catch(C) { } } — _end example_]

[Note 7:

If a destructor directly invoked by stack unwinding exits via an exception,std​::​terminate is invoked.

— _end note_]

14.3 Stack unwinding [except.ctor]

As control passes from the point where an exception is thrown to a handler, objects are destroyed by a process, specified in this subclause, called stack unwinding.

Each object with automatic storage duration is destroyed if it has been constructed, but not yet destroyed, since the try block was entered.

If an exception is thrown during the destruction of temporaries or local variables for a return statement ([stmt.return]), the destructor for the returned object (if any) is also invoked.

The objects are destroyed in the reverse order of the completion of their construction.

[Example 1: struct A { };struct Y { ~Y() noexcept(false) { throw 0; } }; A f() { try { A a; Y y; A b;return {}; } catch (...) { } return {}; }

At #1, the returned object of type A is constructed.

Then, the local variable b is destroyed ([stmt.jump]).

Next, the local variable y is destroyed, causing stack unwinding, resulting in the destruction of the returned object, followed by the destruction of the local variable a.

Finally, the returned object is constructed again at #2.

— _end example_]

If the initialization of an object other than by delegating constructor is terminated by an exception, the destructor is invoked for each of the object's subobjects that were known to be initialized by the object's initialization and whose initialization has completed ([dcl.init]).

[Note 1:

If such an object has a reference member that extends the lifetime of a temporary object, this ends the lifetime of the reference member, so the lifetime of the temporary object is effectively not extended.

— _end note_]

[Note 2:

This includes virtual base class subobjects if the initialization is for a complete object, and can include variant members that were nominated explicitly by a mem-initializer or designated-initializer-clause or that have a default member initializer.

— _end note_]

If the destructor of an object is terminated by an exception, each destructor invocation that would be performed after executing the body of the destructor ([class.dtor]) and that has not yet begun execution is performed.

[Note 3:

This includes virtual base class subobjects if the destructor was invoked for a complete object.

— _end note_]

The subobjects are destroyed in the reverse order of the completion of their construction.

Such destruction is sequenced before entering a handler of the function-try-block of the constructor or destructor, if any.

If the compound-statementof the function-bodyof a delegating constructor for an object exits via an exception, the object's destructor is invoked.

Such destruction is sequenced before entering a handler of thefunction-try-block of a delegating constructor for that object, if any.

14.4 Handling an exception [except.handle]

Theexception-declarationin ahandlerdescribes the type(s) of exceptions that can cause thathandlerto be entered.

Theexception-declarationshall not denote an incomplete type, an abstract class type, or an rvalue reference type.

Theexception-declarationshall not denote a pointer or reference to an incomplete type, other than “pointer to cv void”.

A handler of type“array of T” orfunction type Tis adjusted to be of type “pointer to T”.

Ahandleris a match for an exception object of typeEif

[Note 1:

Athrow-expressionwhose operand is an integer literal with value zero does not match a handler of pointer or pointer-to-member type.

A handler of reference to array or function type is never a match for any exception object ([expr.throw]).

— _end note_]

[Example 1: class Matherr { virtual void vf(); };class Overflow: public Matherr { };class Underflow: public Matherr { };class Zerodivide: public Matherr { };void f() { try { g();} catch (Overflow oo) { } catch (Matherr mm) { } }

Here, theOverflowhandler will catch exceptions of typeOverflowand theMatherrhandler will catch exceptions of typeMatherrand of all types publicly derived fromMatherrincluding exceptions of typeUnderflowandZerodivide.

— _end example_]

The handlers for a try block are tried in order of appearance.

[Note 2:

This makes it possible to write handlers that can never be executed, for example by placing a handler for a final derived class after a handler for a corresponding unambiguous public base class.

— _end note_]

A...in a handler'sexception-declarationspecifies a match for any exception.

If present, a...handler shall be the last handler for its try block.

If no match is found among the handlers for a try block, the search for a matching handler continues in a dynamically surrounding try block of the same thread.

If the search for a handler exits the function body of a function with a non-throwing exception specification, the function std​::​terminate ([except.terminate]) is invoked.

[Note 3:

An implementation is not permitted to reject an expression merely because, when executed, it throws or might throw an exception from a function with a non-throwing exception specification.

— _end note_]

[Example 2: extern void f(); void g() noexcept { f(); throw 42; }

The call tofis well-formed despite the possibility for it to throw an exception.

— _end example_]

If no matching handler is found, the function std​::​terminate is invoked; whether or not the stack is unwound before this invocation ofstd​::​terminateis implementation-defined ([except.terminate]).

A handler is considered active when initialization is complete for the parameter (if any) of the catch clause.

[Note 4:

The stack will have been unwound at that point.

— _end note_]

Also, an implicit handler is considered active when the function std​::​terminateis entered due to a throw.

A handler is no longer considered active when the catch clause exits.

The exception with the most recently activated handler that is still active is called thecurrently handled exception.

Referring to any non-static member or base class of an object in the handler for afunction-try-blockof a constructor or destructor for that object results in undefined behavior.

Exceptions thrown in destructors of objects with static storage duration or in constructors of objects associated with non-block variables with static storage duration are not caught by afunction-try-blockon the main function.

Exceptions thrown in destructors of objects with thread storage duration or in constructors of objects associated with non-block variables with thread storage duration are not caught by afunction-try-blockon the initial function of the thread.

If a return statement ([stmt.return]) appears in a handler of thefunction-try-blockof a constructor, the program is ill-formed.

The currently handled exception is rethrown if control reaches the end of a handler of thefunction-try-blockof a constructor or destructor.

The variable declared by the exception-declaration, of typecv T or cv T&, is initialized from the exception object, of type E, as follows:

The lifetime of the variable ends when the handler exits, after the destruction of any objects with automatic storage duration initialized within the handler.

When the handler declares an object, any changes to that object will not affect the exception object.

When the handler declares a reference to an object, any changes to the referenced object are changes to the exception object and will have effect should that object be rethrown.

14.5 Exception specifications [except.spec]

The predicate indicating whether a function cannot exit via an exception is called the exception specification of the function.

If the predicate is false, the function has apotentially-throwing exception specification, otherwise it has anon-throwing exception specification.

The exception specification is either defined implicitly, or defined explicitly by using a noexcept-specifieras a suffix of a function declarator.

In a noexcept-specifier, the constant-expression, if supplied, shall be a contextually converted constant expression of type bool ([expr.const]); that constant expression is the exception specification of the function type in which the noexcept-specifier appears.

A ( token that follows noexcept is part of thenoexcept-specifier and does not commence an initializer ([dcl.init]).

[Example 1: void f() noexcept(sizeof(char[2])); void g() noexcept(sizeof(char)); — _end example_]

If a declaration of a function does not have a noexcept-specifier, the declaration has a potentially throwing exception specification unless it is a destructor or a deallocation function or is defaulted on its first declaration, in which cases the exception specification is as specified below and no other declaration for that function shall have a noexcept-specifier.

If a noexcept-specifier is specified in an explicit instantiation, the exception specification shall be the same as the exception specification of all other declarations of that function.

A diagnostic is required only if the exception specifications are not the same within a single translation unit.

If a virtual function has a non-throwing exception specification, all declarations, including the definition, of any function that overrides that virtual function in any derived class shall have a non-throwing exception specification, unless the overriding function is defined as deleted.

[Example 2: struct B { virtual void f() noexcept;virtual void g();virtual void h() noexcept = delete;};struct D: B { void f(); void g() noexcept; void h() = delete; };

The declaration ofD​::​fis ill-formed because it has a potentially-throwing exception specification, whereasB​::​fhas a non-throwing exception specification.

— _end example_]

An expression E ispotentially-throwing if

An implicitly-declared constructor for a class X, or a constructor without a noexcept-specifierthat is defaulted on its first declaration, has a potentially-throwing exception specification if and only if any of the following constructs is potentially-throwing:

[Note 1:

Even though destructors for fully-constructed subobjects are invoked when an exception is thrown during the execution of a constructor ([except.ctor]), their exception specifications do not contribute to the exception specification of the constructor, because an exception thrown from such a destructor would call the function std​::​terminaterather than escape the constructor ([except.throw], [except.terminate]).

— _end note_]

The exception specification for an implicitly-declared destructor, or a destructor without a noexcept-specifier, is potentially-throwing if and only if any of the destructors for any of its potentially constructed subobjects has a potentially-throwing exception specification or the destructor is virtual and the destructor of any virtual base class has a potentially-throwing exception specification.

The exception specification for an implicitly-declared assignment operator, or an assignment-operator without a noexcept-specifierthat is defaulted on its first declaration, is potentially-throwing if and only if the invocation of any assignment operator in the implicit definition is potentially-throwing.

The exception specification for a comparison operator function ([over.binary]) without a noexcept-specifierthat is defaulted on its first declaration is potentially-throwing if and only if any expression in the implicit definition is potentially-throwing.

[Example 3: struct A { A(int = (A(5), 0)) noexcept; A(const A&) noexcept; A(A&&) noexcept;~A();};struct B { B() noexcept; B(const B&) = default; B(B&&, int = (throw 42, 0)) noexcept;~B() noexcept(false);};int n = 7;struct D : public A, public B { int * p = new int[n];};

Furthermore, ifA​::​~A()were virtual, the program would be ill-formed since a function that overrides a virtual function from a base class shall not have a potentially-throwing exception specification if the base class function has a non-throwing exception specification.

— _end example_]

An exception specification is considered to be needed when:

The exception specification of a defaulted function is evaluated as described above only when needed; similarly, thenoexcept-specifier of a specialization of a templated function is instantiated only when needed.

14.6 Special functions [except.special]

14.6.1 General [except.special.general]

The function std​::​terminate ([except.terminate]) is used by the exception handling mechanism for coping with errors related to the exception handling mechanism itself.

The function std​::​uncaught_exceptions ([uncaught.exceptions]) reports how many exceptions are uncaught in the current thread.

The function std​::​current_exception ([propagation]) and the classstd​::​nested_exception ([except.nested]) can be used by a program to capture the currently handled exception.

14.6.2 The std​::​terminate function [except.terminate]

Some errors in a program cannot be recovered from, such as when an exception is not handled or a std​::​thread object is destroyed while its thread function is still executing.

[Note 1:

These situations are:

— _end note_]

In the situation where no matching handler is found, it isimplementation-defined whether or not the stack is unwound before std​::​terminate is invoked.

In the situation where the search for a handler ([except.handle]) exits the function body of a function with a non-throwing exception specification ([except.spec]), it isimplementation-defined whether the stack is unwound, unwound partially, or not unwound at all before the function std​::​terminate is invoked.

In all other situations, the stack shall not be unwound before the function std​::​terminateis invoked.

An implementation is not permitted to finish stack unwinding prematurely based on a determination that the unwind process will eventually cause an invocation of the functionstd​::​terminate.