SyntaxStudy
Sign Up
C++ Exception Safety Guarantees and noexcept
C++ Beginner 1 min read

Exception Safety Guarantees and noexcept

The C++ exception safety taxonomy defines four guarantees for operations. The no-throw guarantee means the operation will never throw. The strong guarantee means that if an operation throws, the program state is unchanged — as if the operation were never called. The basic guarantee means that no resources are leaked and invariants are maintained, but the state may have changed. No guarantee means anything can happen, including leaks and corrupt state. Writing exception-safe code requires deliberate design. The copy-and-swap idiom is the standard technique for providing the strong guarantee in assignment operators: create a copy of the new state, swap it into 'this', and let the old state be destroyed by the copy's destructor. If creating the copy throws, 'this' is unmodified. The 'noexcept' specifier can be conditioned on a boolean expression: 'noexcept(expr)'. This allows a function template to inherit the noexcept property of the operations it calls, using 'noexcept(noexcept(...))' — the outer 'noexcept' as specifier and the inner as operator. This is the pattern used by standard library implementations to make generic containers as efficient as possible.
Example
#include <iostream>
#include <utility>   // std::swap
#include <string>
#include <vector>

class SafeBuffer {
public:
    explicit SafeBuffer(std::size_t n)
        : data_(new int[n]), size_(n) {}

    // Destructor — no-throw guarantee
    ~SafeBuffer() noexcept { delete[] data_; }

    // Copy constructor — basic guarantee (may throw on alloc)
    SafeBuffer(const SafeBuffer& other)
        : data_(new int[other.size_]), size_(other.size_) {
        std::copy(other.data_, other.data_ + size_, data_);
    }

    // Copy-and-swap idiom — strong guarantee on assignment
    SafeBuffer& operator=(SafeBuffer other) noexcept {
        swap(*this, other);   // no-throw swap
        return *this;
    }

    // Move constructor — no-throw guarantee
    SafeBuffer(SafeBuffer&& other) noexcept
        : data_(other.data_), size_(other.size_) {
        other.data_ = nullptr;
        other.size_ = 0;
    }

    friend void swap(SafeBuffer& a, SafeBuffer& b) noexcept {
        std::swap(a.data_, b.data_);
        std::swap(a.size_, b.size_);
    }

    std::size_t size() const noexcept { return size_; }

private:
    int*        data_;
    std::size_t size_;
};

// noexcept propagation in template
template<typename T>
void conditionalSwap(T& a, T& b)
    noexcept(noexcept(std::swap(a, b))) {
    std::swap(a, b);
}

int main() {
    SafeBuffer a(4), b(8);
    a = b;   // strong guarantee via copy-and-swap
    std::cout << "a.size() after assignment = " << a.size() << "\n";

    int x = 1, y = 2;
    conditionalSwap(x, y);
    std::cout << "x=" << x << " y=" << y << "\n";

    return 0;
}