1. Which operational guarantee does a function offer if it satisfies the “Strong Exception Guarantee”?
Answer: Commit-or-rollback: if an exception is thrown, all state changes are rolled back, leaving the program in exactly the state it was before the call.
The strong guarantee is the strictest of the three exception safety levels (alongside basic and no-throw). Its contract: either the operation completes fully and the new state takes effect, or an exception is thrown and the state is unchanged — as if the call never happened.
Implementing it typically means doing the dangerous work on copies or temporaries first (for example, building a new buffer, then swapping it into place), and only committing to the visible state with a no-fail operation like swap. If construction throws partway, the original state is still intact because nothing visible was touched yet.
Contrast with the basic guarantee, which only promises no resource leaks and that objects remain in a valid (but possibly modified) state after an exception. The strong guarantee is stricter: not just valid, but identical.
The interview answer: strong exception guarantee = commit-or-rollback; on exception, state is fully rolled back to the pre-call condition.
Answer:
Commit-or-rollback: if an exception is thrown, all state changes are rolled back, leaving the program in exactly the state it was before the call.
The strong guarantee is the strictest of the three exception safety levels (alongside basic and no-throw). Its contract: either the operation completes fully and the new state takes effect, or an exception is thrown and the state is unchanged — as if the call never happened.
Implementing it typically means doing the dangerous work on copies or temporaries first (for example, building a new buffer, then swapping it into place), and only committing to the visible state with a no-fail operation like swap. If construction throws partway, the original state is still intact because nothing visible was touched yet.
Contrast with the basic guarantee, which only promises no resource leaks and that objects remain in a valid (but possibly modified) state after an exception. The strong guarantee is stricter: not just valid, but identical.
The interview answer: strong exception guarantee = commit-or-rollback; on exception, state is fully rolled back to the pre-call condition.
2. What happens when an exception is thrown from a class destructor during an active stack unwinding phase caused by another exception?
Answer: std::terminate() is called immediately.
C++ cannot handle two exceptions simultaneously in flight. When an exception is being unwound (the first exception is propagating, destructors are running), if a destructor during that unwinding throws an exception of its own, the program can’t continue unwinding — there’s no mechanism to track both. The runtime calls std::terminate(), which by default aborts the program.
This is why destructors must not throw, and why modern C++ makes destructors noexcept by default: a destructor that throws while unwinding is a guaranteed terminate.
The engineering takeaway: destructors should be written to swallow or handle errors internally (log, clean up, but never propagate). The interview answer: the runtime immediately calls std::terminate() — two concurrent exceptions aren’t supported.
Answer:
std::terminate() is called immediately.
C++ cannot handle two exceptions simultaneously in flight. When an exception is being unwound (the first exception is propagating, destructors are running), if a destructor during that unwinding throws an exception of its own, the program can’t continue unwinding — there’s no mechanism to track both. The runtime calls std::terminate(), which by default aborts the program.
This is why destructors must not throw, and why modern C++ makes destructors noexcept by default: a destructor that throws while unwinding is a guaranteed terminate.
The engineering takeaway: destructors should be written to swallow or handle errors internally (log, clean up, but never propagate). The interview answer: the runtime immediately calls std::terminate() — two concurrent exceptions aren’t supported.
3. What happens if a function marked noexcept throws an unhandled exception at runtime?
Answer: std::terminate() is invoked immediately — without full stack unwinding.
noexcept is a promise: “this function will not propagate an exception.” If that promise is broken — an exception escapes the function — the runtime calls std::terminate() directly. terminate is the ultimate handler; by default it aborts the program.
The critical detail: because the compiler and runtime treat noexcept as a hard contract, stack unwinding is not performed. Destructors of local objects in the noexcept function do not necessarily run. This is why noexcept must not be applied lightly to functions that can throw — it turns a recoverable error into a hard abort and skips cleanup.
The interview answer: an escaping exception from a noexcept function calls std::terminate() immediately, without unwinding.
Answer:
std::terminate() is invoked immediately — without full stack unwinding.
noexcept is a promise: “this function will not propagate an exception.” If that promise is broken — an exception escapes the function — the runtime calls std::terminate() directly. terminate is the ultimate handler; by default it aborts the program.
The critical detail: because the compiler and runtime treat noexcept as a hard contract, stack unwinding is not performed. Destructors of local objects in the noexcept function do not necessarily run. This is why noexcept must not be applied lightly to functions that can throw — it turns a recoverable error into a hard abort and skips cleanup.
The interview answer: an escaping exception from a noexcept function calls std::terminate() immediately, without unwinding.
4. What is the runtime effect of calling std::terminate()?
Answer: Program execution ends immediately, without stack unwinding — object destructors do not run.
std::terminate() is the runtime’s last-resort handler, invoked when exception handling has failed beyond recovery: an exception escapes a noexcept function, an exception is thrown during active unwinding, or an uncaught exception escapes main’s try/catch framework. It calls the registered terminate handler — by default std::abort() — which terminates the process.
The critical consequence: no stack unwinding is performed. Local objects’ destructors are not called. Anything relying on destructors for cleanup (file handles, mutexes, memory) is lost — which is exactly why a terminated program is a hard abort rather than a graceful shutdown.
The interview answer: std::terminate() aborts immediately (via std::abort() by default), skipping stack unwinding and destructor calls.
Answer:
Program execution ends immediately, without stack unwinding — object destructors do not run.
std::terminate() is the runtime’s last-resort handler, invoked when exception handling has failed beyond recovery: an exception escapes a noexcept function, an exception is thrown during active unwinding, or an uncaught exception escapes main’s try/catch framework. It calls the registered terminate handler — by default std::abort() — which terminates the process.
The critical consequence: no stack unwinding is performed. Local objects’ destructors are not called. Anything relying on destructors for cleanup (file handles, mutexes, memory) is lost — which is exactly why a terminated program is a hard abort rather than a graceful shutdown.
The interview answer: std::terminate() aborts immediately (via std::abort() by default), skipping stack unwinding and destructor calls.
5. What causes a stack overflow error during deep recursive calls?
Answer: The cumulative memory of nested call-stack frames exceeds the thread’s fixed stack limit (typically 1–8 MB).
Every function call pushes a stack frame — return address, saved registers, local variables — onto the thread’s call stack, which has a fixed, OS-allocated size (commonly 1–8 MB). Deep (especially unbounded) recursion keeps pushing frames without returning, until the stack region is exhausted. At that point the program faults with a stack overflow (in practice, SIGSEGV or an unhandled exception).
Note it’s the stack, not the heap, that’s exhausted — local data lives on the stack; only heap allocations grow dynamically. The fixes: convert recursion to iteration, use explicit heap-based stacks, or (when recursion is genuinely required and depth-bounded) increase the thread stack size. The interview answer: nested stack frames exceed the thread’s fixed stack limit, causing a stack overflow fault.
Answer:
The cumulative memory of nested call-stack frames exceeds the thread’s fixed stack limit (typically 1–8 MB).
Every function call pushes a stack frame — return address, saved registers, local variables — onto the thread’s call stack, which has a fixed, OS-allocated size (commonly 1–8 MB). Deep (especially unbounded) recursion keeps pushing frames without returning, until the stack region is exhausted. At that point the program faults with a stack overflow (in practice, SIGSEGV or an unhandled exception).
Note it’s the stack, not the heap, that’s exhausted — local data lives on the stack; only heap allocations grow dynamically. The fixes: convert recursion to iteration, use explicit heap-based stacks, or (when recursion is genuinely required and depth-bounded) increase the thread stack size. The interview answer: nested stack frames exceed the thread’s fixed stack limit, causing a stack overflow fault.
6. What is the purpose of std::unreachable() introduced in C++23?
Answer: It tells the compiler that execution cannot reach this point, enabling aggressive optimizations.
std::unreachable() is an explicit UB hook: you assert “this code is genuinely unreachable.” It’s used after exhaustive dispatch where the compiler can’t prove totality — e.g., after a fully-covered switch, an infinite loop, or an exhaustive if/else chain:
switch (x) {
case 0: return a;
case 1: return b;
default: std::unreachable(); // logically can't happen
}
By asserting unreachability, the compiler can eliminate the default branch, dead-code the fall-through, and assume stronger invariants downstream — better codegen. (If you violate the assertion, that’s UB.) It’s a replacement for the old __builtin_unreachable() idiom. The interview answer: an explicit unreachable-assertion that lets the compiler remove branches and optimize assuming the point is never hit.
Answer:
It tells the compiler that execution cannot reach this point, enabling aggressive optimizations.
std::unreachable() is an explicit UB hook: you assert “this code is genuinely unreachable.” It’s used after exhaustive dispatch where the compiler can’t prove totality — e.g., after a fully-covered switch, an infinite loop, or an exhaustive if/else chain:
switch (x) {
case 0: return a;
case 1: return b;
default: std::unreachable(); // logically can't happen
}
By asserting unreachability, the compiler can eliminate the default branch, dead-code the fall-through, and assume stronger invariants downstream — better codegen. (If you violate the assertion, that’s UB.) It’s a replacement for the old __builtin_unreachable() idiom. The interview answer: an explicit unreachable-assertion that lets the compiler remove branches and optimize assuming the point is never hit.
7. What is the effect of invoking std::abort() in a C++ program?
Answer: The program terminates immediately with SIGABRT, skipping destructors and cleanup for automatic, thread-local, and static objects.
std::abort() raises SIGABRT and terminates the process abnormally:
- No stack unwinding — local (automatic) objects’ destructors do not run.
- No static/thread-local cleanup — those destructors are skipped too.
- No
return 0— the process ends with an abnormal-termination signal.
Unlike std::exit() (which runs atexit handlers and static destructors), abort() is the hard kill — useful when the program is in a state too corrupt to trust any cleanup. The interview answer: immediate abnormal termination via SIGABRT, bypassing destructors and unwinding entirely.
Answer:
The program terminates immediately with SIGABRT, skipping destructors and cleanup for automatic, thread-local, and static objects.
std::abort() raises SIGABRT and terminates the process abnormally:
- No stack unwinding — local (automatic) objects’ destructors do not run.
- No static/thread-local cleanup — those destructors are skipped too.
- No
return 0— the process ends with an abnormal-termination signal.
Unlike std::exit() (which runs atexit handlers and static destructors), abort() is the hard kill — useful when the program is in a state too corrupt to trust any cleanup. The interview answer: immediate abnormal termination via SIGABRT, bypassing destructors and unwinding entirely.
Premium Content
Unlock Exceptions & Error Handling and all premium lessons with a subscription.
From ₹199.99/year — See plans