What Happens When std::unreachable Is Executed in C++?
Introduced in C++23 in the <utility> header,
std::unreachable() informs the compiler that a specific
path of code cannot be executed under valid program semantics. If
execution actually reaches a branch containing
std::unreachable() at runtime, the program invokes
undefined behavior. Because the compiler assumes this scenario is
logically impossible, it generates no recovery mechanisms, which can
lead to application crashes, data corruption, erratic execution paths,
or silent failures.
The Mechanism of Undefined Behavior
Unlike functions such as std::abort() or
std::terminate(), std::unreachable() does not
actively stop the program or produce an error message by default.
Instead, it functions as a promise to the compiler's optimizer.
When the optimizer processes std::unreachable(), it
operates under the absolute assumption that control flow can never reach
that call. Consequently, the compiler uses this assumption to optimize
surrounding code, often by eliminating conditional checks, skipping
boundary validations, or reordering assembly instructions.
If control flow enters an unreachable branch at runtime, the actual outcome depends entirely on how the compiler structured the surrounding binary:
- Instruction Fallthrough: The CPU may continue executing whatever instructions happen to be placed next in memory, whether that is the body of an unrelated adjacent function, an invalid instruction, or arbitrary data interpreted as machine code.
- Segmentation Faults: If the instruction pointer encounters non-executable memory or dereferences invalid pointers that the compiler assumed were safe, the operating system will forcefully terminate the process with a segmentation fault or access violation.
- Optimization Anomalies (Time Travel Bugs): Because the compiler assumes the path cannot be executed, it may eliminate preceding operations that are deemed redundant. If the path is reached, program state prior to the call may appear corrupted or logically inverted.
Contrast with Debug Assertions and Aborts
It is critical not to confuse std::unreachable() with
defensive assertions:
assert(false)evaluates only in debug builds (whenNDEBUGis not defined), printing an error message and halting execution. In release builds,assertis completely stripped out.std::abort()guarantees an immediate, uncatchable termination of the process in both debug and release configurations.std::unreachable()generates no safe termination code. In an optimized release build, calling it when the condition is violated results strictly in undefined behavior.
Detecting std::unreachable at Runtime
Because tracking undefined behavior during production runs is difficult, compilers provide tools to catch violations during development:
- Undefined Behavior Sanitizer (UBSan): Compiling
with
-fsanitize=undefinedin GCC or Clang transformsstd::unreachable()into a trapped runtime check. If an unreachable branch is entered, the program prints a diagnostic report pinpointing the exact file and line number, then halts. - Debug Mode Built-ins: Many standard library
implementations map
std::unreachable()to compiler-specific built-ins like__builtin_unreachable()in GCC/Clang or__assume(false)in MSVC. In non-optimized builds with specific debug flags enabled, compilers may insert trap instructions (such asUD2on x86) to cause an immediate hardware fault rather than silent memory corruption.