15 Exception handling [except]

15.1 Throwing an exception [except.throw]

Throwing an exception transfers control to a handler. [ Note: An exception can be thrown from one of the following contexts: throw-expression (see below), allocation functions ([basic.stc.dynamic.allocation]), dynamic_cast ([expr.dynamic.cast]), typeid ([expr.typeid]), new-expression ([expr.new]), and standard library functions ([structure.specifications]).  — end note ] 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 const 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 ]

When an exception is thrown, control is transferred to the nearest handler with a matching type ([except.handle]); “nearest” means the handler for which the compound-statement or ctor-initializer following the try keyword was most recently entered by the thread of control and not yet exited.

Throwing an exception copy-initializes ([dcl.init], [class.copy]) a temporary object, called the exception object. The temporary is an lvalue and is used to initialize the variable declared in the matching handler ([except.handle]). If the type of the exception object would be an incomplete type or a pointer to an incomplete type other than (possibly cv-qualified) void the program is ill-formed. Evaluating a throw-expression with an operand throws an exception; the type of the exception object is determined by removing any top-level cv-qualifiers from the static type of the operand and adjusting the type from “array of T” or “function returning T” to “pointer to T” or “pointer to function returning T,” respectively.

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. The exception object is destroyed after either the last remaining active handler for the exception exits by any means other than rethrowing, or the last object of type std::exception_ptr ([propagation]) that refers to the exception object is destroyed, whichever is later. In the former case, the destruction occurs when the handler exits, immediately after the destruction of the object declared in the exception-declaration in the handler, if any. In the latter case, the destruction occurs before the destructor of std::exception_ptr returns. The implementation may then deallocate the memory for the exception object; any such deallocation is done in an unspecified way. [ Note: a thrown exception does not propagate to other threads unless caught, stored, and rethrown using appropriate library functions; see [propagation] and [futures].  — end note ]

When the thrown object is a class object, the constructor selected for the copy-initialization and the destructor shall be accessible, even if the copy/move operation is elided ([class.copy]).

An exception is considered caught when a handler for that exception becomes active ([except.handle]). [ Note: An exception can have active handlers and still be considered uncaught if it is rethrown.  — end note ]

If the exception handling mechanism, after completing the initialization of the exception object but before the activation of a handler for the exception, calls a function that exits via an exception, std::terminate is called ([except.terminate]). [ Example:

struct C {
  C() { }
  C(const C&) {
    if (std::uncaught_exception()) {
      throw 0;      // throw during copy to handler's exception-declaration object ([except.handle])
    }
  }
};

int main() {
  try {
    throw C();      // calls std::terminate() if construction of the handler's
                    // exception-declaration object is not elided ([class.copy])
  } catch(C) { }
}

 — end example ]

A throw-expression with no operand rethrows the currently handled exception ([except.handle]). The exception is reactivated with the existing exception object; no new exception object is created. The exception is no longer considered to be caught; therefore, the value of std::uncaught_exception() will again be true. [ 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 ]

If no exception is presently being handled, executing a throw-expression with no operand calls std::terminate() ([except.terminate]).