uC++ EHM

Destructor Exception

Destructors are implicitly noexcept ⇒ they cannot raise an exception. To allow a destructor to raise, mark it (or a base class’s destructor) noexcept(false).

Why the default restriction?

Because destructors run during stack unwinding from another exception — and uC++ (like C++) doesn’t allow two exceptions to propagate simultaneously. If a destructor threw during unwinding, the runtime couldn’t decide which exception to keep. So the safe default is “no throws from destructors” and you have to opt in explicitly.

Allowing destructor raises

struct E {};
struct C {
    ~C() noexcept(false) { throw E(); }   // opt-in
};
try {                     // outer try
    C x;                  // raise on deallocation
    try {                 // inner try
        C y;              // raise on deallocation
    } catch( E ) { ... }  // inner handler — catches y's destructor throw
} catch( E ) { ... }      // outer handler — catches x's destructor throw

Only starts propagation, never intervenes

A destructor cannot raise during an ongoing propagation — it can only start propagation:

try {
    C x;              // raise on deallocation
    throw E();        // raise of E unwinds the try block
} catch( E ) { ... }

When throw E() unwinds, x’s destructor runs and tries to raise another E. Which one wins? That’s why uncaught_exceptions() exists — use it to check if an exception is already propagating and skip the raise if so.

Timeline constraint (two exceptions can’t coexist)

  • Cannot start a second exception without a handler for the first (cannot drop).
  • Cannot postpone the first (the second might remove its handlers during unwinding).
  • ⇒ Destructor code is the only place that can intervene during propagation, but even it can’t raise during — only start.