5.2.30 · D5C++ Programming

Question bank — noexcept specifier

1,198 words5 min readBack to topic

Every item is one line: a claim/question, then :::, then the reasoning. The reasoning is the point — never accept a bare "yes/no".


True or false — justify

A noexcept function is guaranteed not to crash.
False — noexcept only promises no exception escapes; the function can still segfault, deadlock, or infinite-loop. See noexcept specifier: it's a throw-promise, not a safety guarantee.
noexcept alone means exactly the same as noexcept(true).
True — writing noexcept with no parentheses is defined to be shorthand for noexcept(true), the "never throws" promise.
An ordinary function with no specifier is treated as noexcept(true).
False — the default for ordinary functions is noexcept(false) (may throw); only special members like destructors are implicitly noexcept(true).
If a noexcept function throws internally but catches it before returning, the contract is broken.
False — only an exception that escapes the function triggers std::terminate; a try/catch that swallows it fully is perfectly legal.
Marking every function noexcept is a free performance win.
False — if such a function can actually throw, you've planted a std::terminate landmine; a false promise turns a recoverable error into an instant crash.
The noexcept(expr) operator actually runs expr to see if it throws.
False — expr is unevaluated (like sizeof); the operator asks a compile-time question about the declared throwing behaviour, nothing executes.
A destructor that throws in C++11 will call std::terminate.
True — destructors are implicitly noexcept since C++11, so an escaping throw violates the promise and terminates. See Destructors.
std::vector always moves elements when it reallocates.
False — it only moves when the element's move constructor is noexcept; otherwise it copies to preserve the Strong exception guarantee.
noexcept(noexcept(x)) contains a typo — you can't nest the keyword.
False — this is intentional: the outer is the specifier, the inner is the operator computing the bool for it. Two roles, one keyword.
A noexcept specifier makes the compiler generate exception-handling tables just in case.
False — the whole payoff is the opposite: the compiler can skip the unwinding machinery here, which is exactly why it must terminate rather than unwind if you lie.

Spot the error

void f() noexcept { throw std::runtime_error("x"); } — what's wrong?
The exception escapes a noexcept function, so std::terminate fires immediately; a lying promise, not a compile error.
std::vector<T> v; speeds up on resize just because T has a move constructor.
The move constructor existing isn't enough — it must be noexcept, otherwise move_if_noexcept falls back to copying.
constexpr bool b = noexcept(risky()); where int risky() { throw 1; } — is b true?
b is false; risky has no noexcept promise (default may-throw), so the operator must assume it could throw. See constexpr.
~S() noexcept(false) { throw 1; } "fixes" a throwing destructor.
It compiles and won't call terminate on its own, but throwing from a destructor during stack unwinding of another exception is still lethal — the real fix is to not throw.
A generic swap written noexcept(true) unconditionally is correct.
Wrong — if T's move can throw, this false promise arms terminate; you need the conditional form noexcept(noexcept(...)).
void g() throw(); is the modern way to say "never throws".
throw() is the deprecated/removed C++98 dynamic exception specification; use noexcept instead.

Why questions

Why does a broken noexcept promise call terminate instead of just rethrowing?
Because the compiler was allowed to omit the unwinding tables for that function; there may be no info to unwind with, so stopping is the only safe choice.
Why does std::vector copy (not move) elements of a throw-move type on reallocation?
If a move threw halfway, some elements would be in the new buffer and some lost — corrupt state. Copying leaves the originals intact if a copy throws, upholding the Strong exception guarantee.
Why is the noexcept operator's argument left unevaluated?
The question is "could this throw?", answerable from declarations at compile time; running it would defeat the purpose and cause side effects. It mirrors how sizeof works.
Why were destructors made implicitly noexcept in C++11?
A destructor throwing while the stack is already unwinding from another exception is undefined chaos, so the language forbids escape by default. See Move semantics and Destructors.
Why does one keyword ("is your move noexcept?") sometimes double vector resize speed?
It switches the container from copying every element to moving them — moves reuse existing buffers instead of duplicating them, so growth becomes O(pointers) not O(deep copies).
Why can't the compiler just figure out whether a function throws by itself?
Analysis across separate compilation units and virtual/indirect calls is undecidable in general, so the language relies on the programmer's explicit promise instead.

Edge cases

Does noexcept(true) on a function that calls a may-throw helper make the whole thing safe?
No — if that helper's exception escapes, you still hit terminate; the specifier doesn't magically stop the inner throw, it just changes the punishment.
What is noexcept(expr) when expr is something that literally cannot compile (e.g. undeclared)?
It's ill-formed, not false — the operator needs a well-formed expression; only throwing behaviour is what it evaluates, not validity.
Is a function noexcept if every operation inside it is noexcept but it's not marked?
No — without the specifier the function itself is noexcept(false); being throw-free internally doesn't auto-promote its declared spec.
What happens with noexcept(noexcept(T(std::move(a)))) when T's move is deleted?
The inner expression is ill-formed, so the specifier is ill-formed too; the type simply won't satisfy that swap, rather than quietly becoming may-throw.
Can a noexcept function contain a throw statement at all and still be valid code?
Yes — as long as that exception is caught before it escapes; a bare throw x; with no surrounding catch would leak and terminate at runtime.
If move is not noexcept but the type has no copy constructor either, what does vector do on resize?
It is forced to move (there's no copy fallback), so a throwing move can break the strong guarantee for that type — a known trade-off, not a noexcept violation.

Recall One-line self-test

The single sentence that resolves 80% of these traps ::: noexcept is a promise about escape, enforced by terminate, exploited by the compiler to drop unwinding machinery — nothing more, nothing less.