______________________________________________________________________
15 Exception handling [except]
______________________________________________________________________
1 Exception handling provides a way of transferring control and informa
tion from a point in the execution of a program to an exception han
dler associated with a point previously passed by the execution. A
handler will be invoked only by a throw-expression invoked in code
executed in the handler's try-block or in functions called from the
handler's try-block.
try-block:
try compound-statement handler-seq
function-try-block:
try ctor-initializeropt function-body handler-seq
handler-seq:
handler handler-seqopt
handler:
catch ( exception-declaration ) compound-statement
exception-declaration:
type-specifier-seq declarator
type-specifier-seq abstract-declarator
type-specifier-seq
...
throw-expression:
throw assignment-expressionopt
A try-block is a statement (_stmt.stmt_). A throw-expression is of
type void. A throw-expression is sometimes referred to as a "throw-
point." Code that executes a throw-expression is said to "throw an
exception;" code that subsequently gets control is called a "handler."
[Note: within this clause "try block" is taken to mean both try-block
and function-try-block. ]
2 A goto, break, return, or continue statement can be used to transfer
control out of a try block or handler, but not into one. When this
happens, each variable declared in the try block will be destroyed in
the context that directly contains its declaration. [Example:
lab: try {
T1 t1;
try {
T2 t2;
if (condition)
goto lab;
} catch(...) { /* handler 2 */ }
} catch(...) { /* handler 1 */ }
Here, executing goto lab; will destroy first t2, then t1, assuming the
condition does not declare a variable. Any exception raised while
destroying t2 will result in executing handler 2; any exception raised
while destroying t1 will result in executing handler 1. ]
3 A function-try-block associates a handler-seq with the ctor-
initializer, if present, and the function-body. An exception thrown
during the execution of the initializer expressions in the ctor-
initializer or during the execution of the function-body transfers
control to a handler in a function-try-block in the same way as an
exception thrown during the execution of a try-block transfers control
to other handlers. [Example:
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)
{
// constructor function body
}
catch (...)
{
// handles exceptions thrown from the ctor-initializer
// and from the constructor function body
}
--end example]
15.1 Throwing an exception [except.throw]
1 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:
throw "Help!";
can be caught by a handler of some char* type:
try {
// ...
}
catch(const char* p) {
// handle character string exceptions here
}
and
class Overflow {
// ...
public:
Overflow(char,double,double);
};
void f(double x)
{
// ...
throw Overflow('+',x,3.45e107);
}
can be caught by a handler for exceptions of type Overflow
try {
// ...
f(1.2);
// ...
}
catch(Overflow& oo) {
// handle exceptions of type Overflow here
}
--end example]
2 When an exception is thrown, control is transferred to the nearest
handler with an appropriate type; "nearest" means the handler whose
try block was most recently entered by the thread of control and not
yet exited; "appropriate type" is defined in _except.handle_.
3 A throw-expression initializes a temporary object of the static type
of the operand of throw, ignoring the top-level cv-qualifiers of the
operand's type, and uses that temporary to initialize the appropri
ately-typed variable named in the handler. Except for the restric
tions on type matching mentioned in _except.handle_ and the use of a
temporary object, the operand of throw is treated exactly as a func
tion argument in a call (_expr.call_) or the operand of a return
statement.
4 The memory for the temporary copy of the exception being thrown is
allocated in an implementation-defined way. The temporary persists as
long as there is a handler being executed for that exception. In par
ticular, if a handler exits by executing a throw; statement, that
passes control to another handler for the same exception, so the tem
porary remains. If the use of the temporary object can be eliminated
without changing the meaning of the program except for the execution
of constructors and destructors associated with the use of the tempo
rary object (_class.temporary_), then the exception in the handler can
be initialized directly with the argument of the throw expression.
When the thrown object is a class object, and the copy constructor
used to initialize the temporary copy is not accessible, the program
is ill-formed (even when the temporary object could otherwise be elim
inated). Similarly, if the destructor for that object is not accessi
ble, the program is ill-formed (even when the temporary object could
otherwise be eliminated).
5 A throw-expression with no operand rethrows the exception being han
dled without copying it. [Example: code that must be executed because
of an exception yet cannot completely handle the exception can be
written like this:
try {
// ...
}
catch (...) { // catch all exceptions
// respond (partially) to exception
throw; // pass the exception to some
// other handler
}
--end example]
6 The exception thrown is the one most recently caught and not finished.
An exception is considered caught when initialization is complete for
the formal parameter of the corresponding catch clause, or when termi
nate() or unexpected() is entered due to a throw. An exception is
considered finished when the corresponding catch clause exits.
7 If no exception is presently being handled, executing a throw-
expression with no operand calls terminate() (_except.terminate_).
15.2 Constructors and destructors [except.ctor]
1 As control passes from a throw-point to a handler, destructors are
invoked for all automatic objects constructed since the try block was
entered. The automatic objects are destroyed in the reverse order of
the completion of their construction.
2 An object that is partially constructed will have destructors executed
only for its fully constructed sub-objects. Should a constructor for
an element of an automatic array throw an exception, only the con
structed elements of that array will be destroyed. If the object or
array was allocated in a new-expression and the new-expression does
not contain a new-placement, the deallocation function
(_basic.stc.dynamic.deallcoation_, _class.free_) is called to free the
storage occupied by the object; the deallocation function is chosen as
specified in _expr.new_. If the object or array was allocated in a
new-expression and the new-expression contains a new-placement, the
storage occupied by the object is deallocated only if an appropriate
placement operator delete is found, as specified in _expr.new_.
3 The process of calling destructors for automatic objects constructed
on the path from a try block to a throw-expression is called "stack
unwinding."
15.3 Handling an exception [except.handle]
1 The exception-declaration in a handler describes the type(s) of excep
tions that can cause that handler to be entered. The exception-
declaration shall not denote an incomplete type. Types shall not be
defined in an exception-declaration.
2 A handler with type T, const T, T&, or const T& is a match for a
throw-expression with an object of type E if
--T and E are the same type (ignoring the top-level cv-qualifiers), or
--T is an unambiguous public base class of E, or
--T is a pointer type and E is a pointer type that can be converted to
T by a standard pointer conversion (_conv.ptr_) not involving con
versions to pointers to private or protected or ambiguous base
classes, or a qualification conversion (_conv.qual_), or a combina
tion of these two.
+------- BEGIN BOX 1 -------+
This implicitly bans handlers of volatile-qualified type. There is no
obvious reason for the restriction, but no one has proposed to remove
it.
+------- END BOX 1 -------+
3 [Example:
class Matherr { /* ... */ virtual vf(); };
class Overflow: public Matherr { /* ... */ };
class Underflow: public Matherr { /* ... */ };
class Zerodivide: public Matherr { /* ... */ };
void f()
{
try {
g();
}
catch (Overflow oo) {
// ...
}
catch (Matherr mm) {
// ...
}
}
Here, the Overflow handler will catch exceptions of type Overflow and
the Matherr handler will catch exceptions of type Matherr and of all
types publicly derived from Matherr including exceptions of type
Underflow and Zerodivide. ]
4 The handlers for a try block are tried in order of appearance. That
makes it possible to write handlers that can never be executed, for
example by placing a handler for a derived class after a handler for a
corresponding base class.
5 A ... in a handler's exception-declaration functions similarly to ...
in a function parameter declaration; it specifies a match for any
exception. If present, a ... handler shall be the last handler for
its try block.
6 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.
7 An exception is considered handled upon entry to a handler. [Note:
the stack will have been unwound at that point. ]
8 If no matching handler is found in a program, the function terminate()
(_except.terminate_) is called. Whether or not the stack is unwound
before calling terminate() is implementation-defined.
9 Referring to any non-static member or base class of an object in the
handler for a function-try-block of a constructor or destructor for
that object results in undefined behavior.
10The fully constructed base classes and members of an object shall be
destroyed before entering the handler of a function-try-block of a
constructor or destructor for that object.
11The scope and lifetime of the parameters of a function or constructor
extend into the handlers of a function-try-block.
12If the handlers of a function-try-block contain a jump into the body
of a constructor or destructor, the program is ill-formed.
13If a return statement appears in a handler of function-try-block of a
constructor, the program is ill-formed.
14The exception being handled is rethrown if control reaches the end of
a handler of the function-try-block of a constructor or destructor.
Otherwise, a function returns when control reaches the end of a han
dler for the function-try-block (_stmt.return_).
15When the catch handler specifies a class object, a copy constructor is
used to initialize a temporary object which is bound to the optionally
specified name in the exception-declaration for the catch handler.
The object shall not have an abstract class type, since objects of
those types shall not be created. That object is destroyed when the
handler is exited, after the destruction of any automatic objects ini
tialized within the handler. The copy constructor and destructor
shall be accessible in the context of the catch handler. If the copy
constructor and destructor are implicitly declared (_class.copy_),
such a use in the catch handler causes these functions to be implic
itly defined; otherwise, the program shall provide a definition for
these functions. If the use of a temporary object can be eliminated
without changing the meaning of the program except for execution of
constructors and destructors associated with the use of the temporary
object, then the optional name can be bound directly to the temporary
(or original) object specified in a throw-expression causing the catch
handler to be executed. The copy constructor and destructor associ
ated with the object shall be accessible even when the temporary
object is eliminated.
16When the catch handler specifies a non-constant object, any changes to
that object which are effected while the handler has not exited are
changes to the temporary copy for the handler and will not affect the
temporary (or original) object that was initialized by execution of
the throw-expression. When the catch handler specifies a reference to
a non-constant object, any changes to the referenced object are
changes to the temporary (or original) object initialized when the
throw expression was executed and will have effect should that object
be rethrown.
15.4 Exception specifications [except.spec]
1 A function declaration lists exceptions that its function might
directly or indirectly throw by using an exception-specification as a
suffix of its declarator.
+------- BEGIN BOX 2 -------+
Should it be possible to use more general types than type-ids in
exception-specifications? In the absence of a proposal for change,
this box will be removed.
+------- END BOX 2 -------+
exception-specification:
throw ( type-id-listopt )
type-id-list:
type-id
type-id-list , type-id
An exception-specification shall appear only on a function declarator
in a function or pointer declaration or definition. An exception-
specification shall not appear in a typedef declaration. [Example:
void f() throw(int); // OK
void (*fp)() throw (int); // OK
void g(void pfa() throw(int)); // OK
typedef int (*pf)() throw(int); // ill-formed
--end example]
2 If any declaration of a function has an exception-specification, all
declarations, including the definition, of that function shall have an
exception-specification with the same set of type-ids. If a virtual
function has an exception-specification, all declarations, including
the definition, of any function that overrides that virtual function
in any derived class shall only allow exceptions that are allowed by
the exception-specification of the base class virtual function.
[Example:
struct B {
virtual void f() throw (int, double);
virtual void g();
};
struct D: B {
void f(); // ill-formed
void g() throw (int); // OK
};
The declaration of D::f is ill-formed because it allows all excep
tions, whereas B::f allows only int and double. ] Similarly, any
function or pointer to function assigned to, or initializing, a
pointer to function shall only allow exceptions that are allowed by
the pointer or function being assigned to or initialized. [Example:
void (*pf1)(); // no exception specification
void (*pf2)() throw(A);
void f()
{
pf1 = pf2; // ok: pf1 is less restrictive
pf2 = pf1; // error: pf2 is more restrictive
}
--end example]
3 In such an assignment or initialization, exception-specifications on
return types and parameter types shall match exactly.
+------- BEGIN BOX 3 -------+
This is needlessly restrictive. We can safely relax this restriction
if needed.
+------- END BOX 3 -------+
4 In other assignments or initializations, exception-specifications
shall match exactly.
+------- BEGIN BOX 4 -------+
This is needlessly restrictive. We can safely relax this restriction
if needed.
+------- END BOX 4 -------+
5 Calling a function through a declaration whose exception-specification
allows other exceptions than those allowed by the exception-
specification of the function's definition is ill-formed. No diagnos
tic is required.
6 Types shall not be defined in exception-specifications.
7 An exception-specification can include the same class more than once
and can include classes related by inheritance, even though doing so
is redundant. An exception-specification can include identifiers that
represent incomplete types. An exception-specification can also
include the name of the predefined class bad_exception.
8 If a class X is in the type-id-list of the exception-specification of
a function, that function is said to allow exception objects of class
X or any class publicly and unambiguously derived from X. Similarly,
if a pointer type Y* is in the type-id-list of the exception-
specification of a function, the function allows exceptions of type Y*
or that are pointers to any type publicly and unambiguously derived
from Y. Otherwise, a function only allows exceptions that have the
same type as the types specified in the type-id-list of its exception-
specification.
9 Whenever an exception is thrown and the search for a handler
(_except.handle_) encounters the outermost block of a function with an
exception-specification, the function unexpected() is called
(_except.unexpected_) if the exception-specification does not allow
the exception. [Example:
class X { };
class Y { };
class Z: public X { };
class W { };
void f() throw (X, Y)
{
int n = 0;
if (n) throw X(); // OK
if (n) throw Z(); // also OK
throw W(); // will call unexpected()
}
--end example]
10The function unexpected() may throw an exception that will satisfy the
exception-specification for which it was invoked, and in this case the
search for another handler will continue at the call of the function
with this exception-specification (see _except.unexpected_), or it may
call terminate.
11An implementation shall not reject an expression merely because when
executed it throws or might throw an exception that the containing
function does not allow. [Example:
extern void f() throw(X, Y);
void g() throw(X)
{
f(); // OK
}
the call to f is well-formed even though when called, f might throw
exception Y that g does not allow. ]
12A function with no exception-specification allows all exceptions. A
function with an empty exception-specification, throw(), does not
allow any exceptions.
13An exception-specification is not considered part of a function's
type.
15.5 Special functions [except.special]
1 The exception handling mechanism relies on two functions, terminate()
and unexpected(), for coping with errors related to the exception han
dling mechanism itself (_lib.support.exception_).
15.5.1 The terminate() function [except.terminate]
1 In the following situations exception handling must be abandoned for
less subtle error handling techniques:
--when a exception handling mechanism, after completing evaluation of
the object to be thrown but before completing the initialization of
the exception-declaration in the matching handler,1) calls a user
function that exits via an uncaught exception,2)
--when the exception handling mechanism cannot find a handler for a
thrown exception (_except.handle_), or
--when the destruction of an object during stack unwinding
(_except.ctor_) exits using an exception, or
--when construction or destruction of a non-local object with static
storage duration exits using an exception (_basic.start.init_), or
--when execution of a function registered with atexit exits using an
exception (_lib.support.start.term_), or
--when a throw-expression with no operand attempts to rethrow an
exception and no exception is being handled (_except.throw_), or
--when unexpected throws an exception which is not allowed by the pre
viously violated exception-specification, and bad_exception is not
included in that exception-specification (_except.unexpected_), or
--when the implementation's default unexpected_handler is called
(_lib.unexpected.handler_), or
--when the implementation's exception handling mechanism encounters
some internal error.
2 In such cases,
void terminate();
is called (_lib.exception.terminate_).
15.5.2 The unexpected() function [except.unexpected]
1 If a function with an exception-specification throws an exception that
is not listed in the exception-specification, the function
void unexpected();
is called (_lib.exception.unexpected_).
_________________________
1) i.e., when uncaught_exception() (_except.uncaught_) returns true.
2) For example, if the object being thrown is of a class with a copy
constructor, terminate() will be called if that copy constructor exits
with an exception during a throw.
2 The unexpected() function shall not return, but it can throw (or re-
throw) an exception. If it throws a new exception which is allowed by
the exception specification which previously was violated, then the
search for another handler will continue at the call of the function
whose exception specification was violated. If it throws or rethrows
an exception that the exception-specification does not allow then the
following happens: if the exception-specification does not include the
name of the predefined exception bad_exception then the function ter
minate() is called, otherwise the thrown exception is replaced by an
implementation-defined object of the type bad_exception and the search
for another handler will continue at the call of the function whose
exception-specification was violated.
3 Thus, an exception-specification guarantees that only the listed
exceptions will be thrown. If the exception-specification includes
the name bad_exception then any exception not on the list may be
replaced by bad_exception within the function unexpected().
15.5.3 The uncaught_exception() function [except.uncaught]
1 The predicate
bool uncaught_exception();
returns true after completing evaluation of the object to be thrown
until completing the initialization of the exception-declaration in
the matching handler (_lib.uncaught_). This includes stack unwinding
(_except.ctor_).
15.6 Exceptions and access [except.access]
1 If the exception-declaration in a catch clause has class type, and the
function in which the catch clause occurs does not have access to the
destructor of that class, the program is ill-formed.
2 An object can be thrown if it can be copied and destroyed in the con
text of the function in which the throw-expression occurs.