C++ for Systems Programmers

noexcept and exception safety

C++ has two main ways to report that an operation failed. One is the C-style approach of returning an error code and trusting the caller to check it; the other is throwing an exception, which immediately unwinds the stack — running destructors as it goes — until a matching catch handler is found. Exceptions keep the success path clean and make it hard to silently ignore an error, but they raise a hard question: when an exception flies through your code mid-operation, what state are your objects left in? That question is the subject of exception safety, and noexcept is the keyword for promising an operation will not throw at all.

Exception safety is usually described as a ladder of guarantees a function can offer. The basic guarantee: if an exception escapes, no resources leak and all objects remain in some valid (if unspecified) state. The strong guarantee: if an exception escapes, the operation has no effect at all — it is as if you never called it (the commit-or-rollback property; vector operations often achieve this by doing work on a copy and swapping at the end). The nothrow guarantee: the operation never throws, full stop. The noexcept specifier marks a function as nothrow; it is both documentation and a contract the compiler can rely on — and if a noexcept function does throw anyway, the program is terminated immediately via std::terminate, no unwinding.

Why it matters: noexcept is not just a promise, it is an enabler. The standard library inspects it — most importantly, std::vector will only move its elements during a reallocation if their move constructor is noexcept; otherwise it falls back to copying, to preserve the strong guarantee (a throw mid-move could leave elements half-transferred and unrecoverable). So marking your move operations noexcept can make your type dramatically faster in containers. The honest caveats: exceptions are roughly zero-cost on the non-throwing path but expensive when actually thrown, which is why many latency-critical and embedded codebases disable them and use error codes instead; and a wrong noexcept (claiming nothrow on something that can throw) does not make it safe — it makes a thrown exception a hard crash.

Buffer(Buffer&& o) noexcept : data(o.data) { o.data = nullptr; } // marking move noexcept lets std::vector move (not copy) Buffers when it grows

A noexcept move constructor is what permits the strong-guarantee container to relocate by moving instead of the safer-but-slower copy.

Lying with noexcept is dangerous: if a function declared noexcept throws anyway, the runtime calls std::terminate and the program dies — there is no recovery. noexcept is a promise the compiler enforces by killing you, not a hint, so only apply it where you are certain nothing can throw.

Also called
exception guaranteeserror handling models例外保證例外安全等級