______________________________________________________________________
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
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.
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. For 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. Any excep
tion raised while destroying t2 will result in executing handler 2;
any exception raised while destroying t1 will result in executing han
dler 1.
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. For 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
try {
// ...
f(1.2);
// ...
}
catch(Overflow& oo) {
// handle exceptions of type Overflow here
}
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 The operand of a throw shall be of a type with no ambiguous base
classes. That is, it shall be possible to convert the value thrown
unambiguously to each of its base classes.1)
4 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. If the static type of the
expression thrown is a class or a pointer or reference to a class,
there shall be an unambiguous conversion from that class type to each
_________________________
1) If the value thrown has no base classes or is not of class type,
this condition is vacuously satisfied.
of its accessible base classes. Except for that restriction and for
the restrictions on type matching mentioned in _except.handle_ and the
use of a temporary variable, the operand of throw is treated exactly
as a function argument in a call (_expr.call_) or the operand of a
return statement.
5 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.
6 A throw-expression with no operand rethrows the exception being han
dled without copying it. For 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
}
7 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.
8 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.
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, the storage occupied by that
object is sometimes deleted also (_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 executed. The exception-
declaration shall not denote an incomplete type.
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
[1]T and E are the same type, or
[2]T is an accessible (_conv.ptr_) base class of E at the throw
point, or
[3]T is a pointer type and E is a pointer type that can be converted
to T by a standard pointer conversion (_conv.ptr_) at the throw
point.
+------- BEGIN BOX 1 -------+
The intent was and is to require no run-time access or ambiguity
checking.
Paragraph 3 of _except.throw_ says that we can't throw an object that
would require the handler mechanism to do ambiguity checks.
Point [2] above says, in particular, that an object with a private
class can be thrown if and only if the thrower has access to that
base. This implies no violation of access, because the thrower could
have thrown the private class directly.
This implies that an exception can be caught by a private class (the
access check, like the ambiguity check is done at the throw point).
This does not require a run-time access check. It does, however,
require that a object of a class with a private base class is trans
mitted to the catch point together with an indication if it can be
caught by its private base class.
It has been suggested that this should be simplified by prohibiting
the throw of an object of a class with a private base class. It has
also been suggested that run-time access checks should be required.
In the absence of a proposal for change, the text will be clarified
along the lines in this box.
+------- END BOX 1 -------+
For 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 all
types publicly derived from Matherr including Underflow and Zerodi
vide.
3 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.
4 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.
5 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.
6 An exception is considered handled upon entry to a handler. The stack
will have been unwound at that point.
7 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.
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 declaration or definition. An exception-specification shall not
appear in a typedef declaration. For 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
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 have an exception-specification at least as
restrictive as that in the base class. For 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 func
tion or pointer to function assigned to, or initializing, a pointer to
function shall have an exception-specification at least as restrictive
as that of the pointer or function being assigned to or initialized.
For 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
}
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
is less restrictive that that of the function's definition is ill-
formed. No diagnostic 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 classes with
ambiguous base classes, even though throwing objects of such classes
is ill-formed (_except.throw_). An exception specification can
include identifiers that represent incomplete types. An exception can
also include the name of the predefined class Xunexpected.
+------- BEGIN BOX 5 -------+
The name Xunexpected is under discussion and will change. The exact
meaning of ``predefined'' and a possible standard library specifica
tion of class Xunexpected is also being defined.
+------- END BOX 5 -------+
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 derived from X. Similarly, if a pointer type
Y* is in the type-id-list of the exception-specification of a func
tion, the function allows exceptions of type Y* or that are pointers
to any type publicly derived from Y*.
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. For 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()
}
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. For 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 Occasionally, exception handling must be abandoned for less subtle
error handling techniques. For example,
--when a exception handling mechanism, after completing evaluation of
the object to be thrown, calls a user function that exits via an
uncaught exception,2)
--when the exception handling mechanism cannot find a handler for a
thrown exception (see _except.handle_),
--when the exception handling mechanism finds the stack corrupted, or
--when a destructor called during stack unwinding caused by an excep
tion tries to exit using an exception.
_________________________
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 In such cases,
void terminate();
is called; terminate() calls the function given on the most recent
call of set_terminate()(_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; unexpected() calls the function given on the most recent
call of set_unexpected()(_lib.exception.unexpected_).
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 an exception which is not allowed by the exception-
specification then the following happens: if the exception-
specification does not include the name of the predefined exception
Xunexpected then the function terminate() is called, otherwise the
thrown exception is replaced by an implementation-defined object of
the type Xunexpected and the search for another handler will continue
at the call of the function whose exception-specification was vio
lated.
3 Thus, an exception-specification guarantees that only the listed
exceptions will be thrown. If the exception-specification includes
the name Xunexpected then any exception not on the list may be
replaced by Xunexpected within the function unexpected().
15.6 Exceptions and access [except.access]
1 The parameter of a catch clause obeys the same access rules as a
parameter of the function in which the catch clause occurs.
2 An object can be thrown if it can be copied and destroyed in the con
text of the function in which the throw occurs.