Exception List
An exception list is part of a routine’s prototype specifying which exception types may propagate out of the routine to its caller.
Why constrain which exceptions a routine can raise?
So callers (and the compiler) know what to expect. A missing handler for an unexpected exception-type is exactly what caused the Ariane 5 rocket to self-destruct — arithmetic overflow in control software, no handler, $370 million loss. Exception lists catch that at compile time (checked types) or at runtime (special failure exception / program termination).
Syntax
int g() throw(E) { ... throw E(); } // "may throw E, nothing else"Two kinds of checking
- Checked/unchecked exception-type — Java, inheritance-based, static check.
- Checked/unchecked routines — C++, list-based, dynamic check (deprecated in C++11, replaced with
noexcept).
Why it hurts reuse
Checked exceptions are useful for engineering rigor but preclude reuse. Consider:
template <class T> void sort( T items[] ) throw( ?, ?, ... ) {
// uses bool operator<( const T&, const T& );
}You can’t know every exception type that < might propagate for every T — so any fixed exception-list excludes some legal types T.
Exception lists and polymorphism break down
// Left example: argument g has signature less restrictive than parameter p
void f( void (*p)() noexcept ) { p(); }
void g() noexcept(false) { throw E(); }
void h() {
try { ... f( g ); ... } catch( E ) {} // precluded — g's signature is wider than p's
}
// Right example: D::g overrides B::g with a wider signature
struct B { virtual void g() noexcept {} };
struct D : public B {
void g() noexcept(false) { throw E(); } // precluded — D::g wider than B::g
};Both are rejected because a callable site that promised “no exceptions” can’t accept a callable that might throw.
Concurrent exceptions
Determining an exception list for a routine can become impossible for concurrent exceptions because they can propagate at any time — see Nonlocal Exception.