When std::terminate Is Invoked in C++: A Complete Guide
In modern C++, std::terminate serves as the runtime's
ultimate fail-safe when normal execution and exception handling
mechanisms collapse. This article explains the core mechanics of
std::terminate, its default behavior, and provides an
exhaustive, straightforward breakdown of the exact language and library
conditions under which the C++ runtime automatically invokes it.
What Is std::terminate?
Defined in the <exception> header,
std::terminate() is an exception handling function called
by the C++ runtime when the program reaches an unrecoverable state where
execution cannot safely proceed.
When invoked, std::terminate() delegates to the active
std::terminate_handler, a function pointer that defaults to
std::abort(). Unlike standard exit functions (such as
std::exit()), std::terminate does not
guarantee stack unwinding, destructors for active local objects may not
execute, and standard exit routines registered via
std::atexit() are bypassed. While you can customize this
handler using std::set_terminate(), the custom handler must
not return; it must terminate the process or execution yields undefined
behavior.
Exact Conditions When std::terminate Is Automatically Invoked
The C++ standard mandates the automatic invocation of
std::terminate under the following explicit conditions:
1. An Exception Is Not Caught
If an exception is thrown and the runtime searches the call stack
without finding a matching catch handler,
std::terminate is called immediately once stack unwinding
reaches the boundaries of main() or the initial function of
a thread.
2. An Exception Is Thrown During Stack Unwinding
If an exception is currently active and traveling up the stack, and a
destructor called during this unwinding process throws an unhandled
exception of its own, the runtime aborts via
std::terminate. C++ allows only one active exception to be
unwound at a time.
3. An Exception
Escapes a noexcept Specification
If a function marked noexcept,
noexcept(true), or throw() (deprecated/removed
in modern standards) exits via an exception, the runtime does not search
for a caller's catch block. Instead, it directly calls
std::terminate().
4. Destruction of
an Active, Joinable std::thread
If a std::thread object's destructor is executed while
the thread is still joinable (i.e., joinable() == true),
std::terminate is called. Developers must explicitly call
either .join() or .detach() on a
std::thread before its lifetime ends.
5. Exceptions Thrown During Static or Thread-Local Destruction
If an exception is thrown and escapes the destructor of a static
object, a namespace-scope variable, or a thread-local object during
program or thread termination, std::terminate is
invoked.
6.
Exceptions Escaping std::atexit or
std::at_quick_exit Handlers
If a function registered with std::atexit() or
std::at_quick_exit() terminates with an uncaught exception,
the runtime handles this failure by invoking
std::terminate.
7. Exceptions in Parallel Execution Policies (C++17)
When using C++ Standard Library parallel algorithms with an execution
policy (e.g., std::execution::par or
std::execution::par_unseq), any exception thrown by an
element access function that escapes the algorithm's invocation triggers
std::terminate.
8. Rethrowing an Empty or Null Exception Pointer
Calling std::rethrow_exception with a null
std::exception_ptr, or invoking throw; when no
exception is currently being handled, directly invokes
std::terminate.
9. Failure Handling Inside a Coroutine Promise (C++20)
In coroutines, if an exception escapes the body of the coroutine and
the promise type's unhandled_exception() member function
itself throws an exception or fails to handle it,
std::terminate is invoked. It is also invoked if an
exception escapes from the initial_suspend() or
final_suspend() awaiters when configured as
noexcept.