______________________________________________________________________ 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-initializer-opt 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. Any excep tion 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. 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 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 restrictions on type matching mentioned in _except.handle_ and the use of a temporary object, the operand of throw is treated exactly as a function 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. 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. 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 [Note: the process of calling destructors for automatic objects con structed 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 a public base class of E, 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_) not involving conversions to pointers to private or protected base classes. [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 Zerodivide. ] 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. [Note: 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. 8 Referring to any non-static member or base class of the object in the handler of a function-try-block of a constructor or destructor of the object results in undefined behavior. 9 The 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. 10The scope and lifetime of the parameters of a function or constructor extend into the handlers of a function-try-block. 11If the handlers of a function-try-block contain a jump into the body of a constructor or destructor, the program is ill-formed. 12If a return statement appears in a handler of function-try-block of a constructor, the program is ill-formed. 13The exception being handled shall be rethrown if control reaches the end of a handler of the function-try-block of a constructor or destructor. Otherwise, the function shall return when control reaches the end of a handler for the function-try-block (_stmt.return_). 14 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. 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. [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 have an exception-specification at least as restrictive as that in the base class. [Example: struct B { virtual void f() throw (int, double); virtual void g(); }; struct D: B { void f(); // ill-formed void g() throw (int); // OK }; --end example] The declaration of D::f is ill-formed because it allows all exceptions, whereas B::f allows only int and double. Simi larly, any function or pointer to function assigned to, or initializ ing, 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. [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. 4 In other assignments or initializations, exception-specifications shall match exactly. 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 identifiers that represent incomplete types. An exception 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*. 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, calls a user function that exits via an uncaught exception,1) --when the exception handling mechanism cannot find a handler for a thrown exception (see _except.handle_), --when the implementation's exception handling mechanism encounters some internal error, or --when an attempt by the implementation to destroy an object during stack unwinding exits using an exception. 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_). 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 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 terminate() 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. _________________________ 1) 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. 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.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 occurs.