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:
  • when an active handler for the exception exits by any means other than rethrowing, immediately after the destruction of the object (if any) declared in the exception-declaration in the handler;
  • when an object of type std​::​exception_­ptr that refers to the exception object is destroyed, before the destructor of std​::​exception_­ptr returns.
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
]