5.2.29C++ Programming

Exception safety — basic, strong, no-throw guarantees

2,011 words9 min readdifficulty · medium4 backlinks

WHY do we even need this?

C++ exceptions can fire from almost anywhere: new throws std::bad_alloc, a copy constructor of a contained type can throw, a comparator can throw. The moment an exception propagates, the stack unwinds — destructors of local objects run, but the half-finished operation stops dead.


WHAT are the three guarantees?

There is implicitly a fourth, the worst case — no guarantee: leaks, corruption, undefined behaviour. We never want this.

Figure — Exception safety — basic, strong, no-throw guarantees

HOW do we achieve each level?

Basic guarantee — use RAII

void basic_ok() {
    auto p = std::make_unique<Widget>();  // owns memory
    p->mutate();        // may throw
    risky();            // may throw
    // if either throws, ~unique_ptr frees the Widget. No leak.
}                       // object valid & destructible: BASIC guarantee

Why this step? unique_ptr's destructor runs during unwinding, so no delete is skipped.

Strong guarantee — copy-and-swap

class Buffer {
    int* data; std::size_t n;
public:
    void append(const int* src, std::size_t k) {        // STRONG
        Buffer tmp(*this);          // 1. copy (may throw — original safe)
        tmp.grow_and_copy(src, k);  // 2. do risky work on tmp (may throw)
        swap(*this, tmp);           // 3. noexcept commit
    }                               // ~tmp frees old data
    friend void swap(Buffer& a, Buffer& b) noexcept {
        std::swap(a.data, b.data);
        std::swap(a.n,    b.n);
    }
};

Why this step? Steps 1 and 2 may throw, but they touch only tmp. If they throw, *this is exactly as before → no effect. Step 3 cannot throw → the commit is atomic.

No-throw guarantee — noexcept operations

void release(Resource* r) noexcept { /* free handles, never throws */ }

A subtle but crucial point: move and the strong guarantee


Worked Examples


Recall Feynman: explain to a 12-year-old

Imagine you're repainting a room. Basic: if you get interrupted, the room is still a usable room — nothing's on fire — but it might be half blue, half white. Strong: you paint a spare wall panel first; only when it's perfectly done do you instantly snap it over the old wall. If you get interrupted, the old wall is still there, untouched — like you never started. No-throw: the job is so simple (flipping a light switch) it can never go wrong. The trick for "strong" is: do all the messy work on a copy, then swap it in at the last safe second.


Active Recall

What are the three exception-safety guarantees, weakest to strongest?
Basic (valid state, no leaks), Strong (commit-or-rollback / no effect on throw), No-throw (never throws, noexcept).
What does the basic guarantee promise on a thrown exception?
No resource leaks and all objects remain in a valid, destructible but unspecified state; invariants hold.
What does the strong guarantee promise?
Commit-or-rollback: if it throws, the operation has no effect — state is exactly as before the call.
Which idiom achieves the strong guarantee and how?
Copy-and-swap: do throwing work on a copy, then swap it in with a noexcept swap so the commit can't throw.
Why must swap, destructors, and moves be noexcept for strong safety?
The commit step in copy-and-swap must not throw; if it did you'd be stuck in an unrecoverable half-done state.
What technique gives the basic guarantee almost for free?
RAII — resources owned by stack objects are released by destructors during stack unwinding.
What happens if a noexcept function throws?
std::terminate() is called immediately; no further stack unwinding occurs.
Why does std::vector copy rather than move during reallocation for some types?
If the move constructor isn't noexcept, a throw mid-move would gut the source and break the strong guarantee, so it copies instead (std::move_if_noexcept).
Why does copy-and-swap assignment handle self-assignment automatically?
The right-hand side is copied (by value) before the swap, so assigning an object to itself just copies then swaps harmlessly.
Strength ordering of the guarantees?
no guarantee < basic < strong < no-throw; each stronger level implies all weaker ones.

Connections

Concept Map

triggers

runs

risks

prevented by

ranks as

ranks as

ranks as

no leaks, valid state

uses

commit or rollback

marked

trivially implies

implied by

Exception thrown mid-operation

Stack unwinding

Local destructors

Leaks, corruption, double-free

Exception safety contract

Basic guarantee

Strong guarantee

No-throw guarantee

RAII ownership

Copy-and-swap

noexcept keyword

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Dekho, exception safety ka matlab hai: agar tumhare function ke beech mein koi exception throw ho jaye (jaise new fail ho gaya, ya copy constructor ne throw kar diya), to tumhara program kis state mein chhoot jaata hai? Iske teen level hain. Basic guarantee ka matlab — koi memory leak nahi hogi aur object usable rahega, par values thodi half-baked ho sakti hain. Strong guarantee matlab "all-or-nothing": ya to pura kaam ho jaye, ya bilkul aisa lage jaise tumne shuru hi nahi kiya (rollback). No-throw matlab function kabhi throw karega hi nahi — yeh noexcept se mark karte hain.

Basic guarantee almost free milti hai agar tum RAII use karo — yaani har resource ek stack object (jaise unique_ptr) ke paas ho, to stack unwind hone par destructor apne aap cleanup kar dega. Leak ka chance hi nahi. Strong guarantee ke liye famous trick hai copy-and-swap: pehle saara risky kaam ek copy (tmp) par karo, aur jab copy poori ready ho jaaye, tab ek noexcept swap se use original mein daal do. Agar beech mein kuch throw hua to sirf tmp kharab hua, original safe — isliye "no effect".

Ek important baat: swap, destructors, aur move operations ko noexcept rakhna zaroori hai, warna commit step hi throw kar gaya to tum recover nahi kar paoge. Isi wajah se std::vector reallocation ke time, agar tumhare type ka move noexcept nahi hai, to woh move ki jagah copy karta hai (slow par safe). Aur dhyaan rakhna — noexcept tabhi lagao jab sach mein guarantee de sako, kyunki jhoot bol diya aur function throw kar gaya to seedha std::terminate() — program turant mar jaayega.

Go deeper — visual, from zero

Test yourself — C++ Programming

Connections