18 Exception handling [except]

18.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-expressions, allocation functions, dynamic_­cast, typeid, new-expressions, 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. An lvalue denoting the temporary 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 cv void 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. All other points happen before that last one. [Note: 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: 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 as well as the constructor selected for a copy-initialization considering the thrown object as an lvalue shall be non-deleted and accessible, even if the copy/move operation is elided. The destructor is potentially invoked.

An exception is considered caught when a handler for that exception becomes active. [Note: 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, std​::​terminate is called. [Example:

struct C {
  C() { }
  C(const C&) {
    if (std::uncaught_exceptions()) {
      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
  } catch(C) { }
}

end example] [Note: Consequently, destructors should generally catch exceptions and not let them propagate. end note]