Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

The noexcept specifier is a C++ language construct that explicitly declares whether a function is permitted to propagate exceptions outside of its scope. Rather than providing a static compile-time guarantee that the function body is exception-free, noexcept establishes a semantic contract dictating runtime behavior: if an exception escapes a function marked noexcept, the program immediately terminates.

Syntax

The specifier is placed in the function declaration, trailing the parameter list and any const or reference qualifiers. It accepts an optional boolean constant expression.
// Unconditional: The function promises not to propagate exceptions.
void func1() noexcept;

// Conditional: Exception specification depends on a compile-time boolean.
void func2() noexcept(true);  // Equivalent to noexcept
void func3() noexcept(false); // May throw (equivalent to omitting noexcept)

// Conditional evaluation using the noexcept operator
template <typename T>
void func4(T x) noexcept(noexcept(x.process())); 

Execution Semantics

The compiler does not statically verify that a noexcept function contains no throwing code; a noexcept function containing a throw statement is syntactically valid and will compile. However, if an exception is thrown within a noexcept function and is not caught internally (i.e., the exception attempts to escape the function boundary), the C++ runtime immediately invokes std::terminate(). When std::terminate() is called in this context, it is implementation-defined whether the C++ runtime performs stack unwinding. Because the compiler is not strictly required to generate stack-unwinding metadata for the scope of a noexcept function, it can optimize the generated code, leading to reduced binary size and potentially faster execution.

Interaction with Move Semantics and Containers

One of the most critical mechanical impacts of noexcept involves move semantics and standard library containers (e.g., std::vector). Containers rely on noexcept to maintain the strong exception guarantee during reallocation. When a container resizes, it must transfer elements to a new memory block. To prevent leaving the container in an unrecoverable, partially-moved state if an exception is thrown mid-transfer, containers utilize std::move_if_noexcept. This utility casts an object to an rvalue reference (enabling a move) if the type’s move constructor is noexcept or if the type is not copy-constructible (e.g., move-only types containing std::unique_ptr). If the move constructor is not noexcept and a copy constructor is available, std::move_if_noexcept casts the object to an lvalue reference, forcing a fallback to the copy constructor. This strong exception guarantee fallback mechanism applies specifically to move constructors. Standard library algorithms (such as std::sort) and container operations (such as std::vector::erase) invoke move assignment unconditionally via std::move. They do not fall back to copy assignment based on the noexcept status of the move assignment operator.

Type System Integration (C++17)

As of C++17, the exception specification is a formal part of the function’s type. This enforces strict type-safety rules regarding function pointers and virtual function overrides.
void throwing_func();
void non_throwing_func() noexcept;

// Valid: Standard function pointer conversion from noexcept to throwing
void (*ptr_throw)() = non_throwing_func;     

// Error: Cannot assign a throwing function to a noexcept pointer
void (*ptr_noexcept)() noexcept = throwing_func; 
When overriding virtual functions, a derived class method can be more restrictive (adding noexcept to a throwing base method) but cannot be less restrictive (removing noexcept from a non-throwing base method).
struct Base {
    virtual void foo() noexcept;
    virtual void bar();
};

struct Derived : Base {
    void foo() override;          // Error: Looser exception specification
    void bar() noexcept override; // Valid: Stricter exception specification
};

Implicit noexcept

The C++ compiler implicitly applies the noexcept specifier to certain special member functions, provided that all corresponding operations on base classes and non-static data members are also noexcept. These include:
  1. Destructors: All user-defined and compiler-generated destructors are implicitly noexcept (unless explicitly marked noexcept(false) or if a base/member destructor is noexcept(false)).
  2. Deallocation Functions: operator delete and operator delete[].
  3. Defaulted Special Member Functions: Default constructors, copy/move constructors, and copy/move assignment operators are implicitly noexcept if the compiler determines that no exceptions can be thrown by the underlying member/base initializations or assignments.

Specifier vs. Operator

The noexcept keyword serves two distinct roles in C++ which are often used together in template metaprogramming:
  1. The Specifier: Applied to a function declaration to dictate its exception-emitting behavior.
  2. The Operator: A compile-time unary operator noexcept(expression) that evaluates to true if the expression is statically determined not to throw an exception, and false otherwise. It does not evaluate the expression at runtime.
// The outer 'noexcept' is the specifier.
// The inner 'noexcept(...)' is the operator evaluating the expression.
void compute() noexcept(noexcept(sub_routine())) {
    sub_routine();
}
Master C++ with Deep Grasping Methodology!Learn More