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 throw operator in C++ is a control flow mechanism used to raise an exception, immediately halting the current execution path and transferring control to the nearest matching exception handler (catch block) in the call stack.

Syntax and Type Properties

The throw operator forms an expression and has two distinct syntactic forms:
throw expression // Form 1: Throws a new exception object
throw            // Form 2: Re-throws the currently handled exception
When appended with a semicolon, the throw expression becomes an expression statement (e.g., throw expression;). A unique syntactic property of the throw operator is that the expression itself evaluates to the type void. This allows it to be embedded within expressions that require a valid operand but do not consume a value, such as the conditional ternary operator:
// The throw expression evaluates to void, making this a valid initialization
int result = isValid ? computeValue() : throw std::runtime_error("Invalid");

Mechanics of throw expression

When a throw expression with an operand is evaluated, the C++ runtime performs the following sequence of operations:
  1. Evaluation and Type Determination: The expression is evaluated. The static type of the expression determines the type of the exception object. Top-level const and volatile qualifiers are discarded, arrays decay to pointers, and functions decay to function pointers.
  2. Exception Object Initialization: A special temporary object, known as the exception object, is allocated in an unspecified, compiler-managed memory region (typically separate from the standard stack or heap). This object is initialized via copy or move construction from the evaluated expression. Crucially, when throwing an object of class type, the copy/move constructor and the destructor of that class must be accessible and non-deleted at the throw site. This is a strict semantic requirement for compilation, even if the compiler ultimately optimizes away (elides) the actual copy or move operation.
  3. Handler Resolution: Before any objects are destroyed, the C++ runtime searches the call stack for a catch block whose parameter type matches the exception object. Implicit conversions are strictly limited; standard conversions (like int to float) are not applied. Only derived-to-base class conversions, adding const/volatile qualifiers, and certain pointer conversions (e.g., to void*) are permitted.
  4. Stack Unwinding or Termination:
    • If a match is found: The runtime initiates stack unwinding up to the scope of the matching handler. It traverses the call stack, invoking the destructors of all fully constructed objects with automatic storage duration in the scopes being exited. Destruction occurs in the reverse order of construction.
    • If no match is found: The runtime immediately calls std::terminate(). In this scenario, whether the stack is unwound at all is implementation-defined.

Mechanics of Parameterless throw (Re-throw)

The parameterless throw expression is syntactically valid and will compile anywhere in a C++ program. Its behavior depends entirely on the runtime context in which it is evaluated:
  • During Exception Handling: If evaluated while an exception is currently being handled (e.g., within a catch block or within a function invoked directly or indirectly from a catch block), it does not create a new exception object. Instead, it resumes the propagation of the existing exception object. This preserves the dynamic type of the original exception. If a derived exception is caught by a base class reference, using throw e; would copy-construct a new exception object of the base type (causing object slicing), whereas throw; propagates the original derived object intact.
  • Outside Exception Handling: If evaluated when no exception is currently active, the runtime immediately invokes std::terminate().
try {
    // ...
} catch (BaseException& e) {
    throw; // Resumes propagation of the original exception object as a statement
}

Interaction with noexcept

The throw operator interacts strictly with function exception specifications. Because handler resolution occurs before stack unwinding, if the search for a handler encounters a function boundary declared with the noexcept specifier (or noexcept(true)), the runtime immediately calls std::terminate().
void strictFunction() noexcept {
    throw 42; // Compiles, but guarantees a call to std::terminate() at runtime
}
In this scenario, stack unwinding does not necessarily begin. It is implementation-defined whether any stack unwinding occurs before the program terminates.
Master C++ with Deep Grasping Methodology!Learn More