C++26 lets you throw and catch at compile time
The keyword constexpr has spent four standards absorbing the rest of the language. C++20 brought virtual calls, dynamic allocation, and much of the algorithm library. It also allowed try blocks, though a throw still could not execute. What stayed outside was actually throwing. Reaching a throw during constant evaluation meant the expression was not a constant expression, so the build failed on the spot.
That mattered more than it sounds, because it split error handling in two. A function meant for both compile time and runtime could not use the language’s own error mechanism. It needed a sentinel value, an optional, or a separate constexpr-only path.
C++26 removes the split. Exceptions can be thrown and caught during constant evaluation. A throw that is caught within the same evaluation is fine. Only an exception that escapes to the top makes the expression non-constant.
A throw caught at compile time
The demo is a checked division that throws std::domain_error on a zero divisor. A wrapper function catches it and returns a sentinel. Both static assertions pass:
static_assert(try_div(10, 2) == 5);
static_assert(try_div(10, 0) == -1); // thrown AND caught during constant evaluation
The second one is the interesting line. At compile time, checked_div throws and the handler in try_div catches it. Evaluation continues, and the result is a perfectly ordinary constant. There is no runtime, no exception tables, and nothing left in the binary.
What it buys you
The practical win is that one function now serves both worlds. A parser, a units conversion, or a validation routine can throw std::invalid_argument and behave correctly whether it runs in a static assertion or at runtime. No logic is duplicated, and no sentinel-value protocol is invented per API.
It also improves the failure message. A throw that escapes constant evaluation is a compile error, and the compiler can name the exception and the throw site. That is considerably better than the wall of instantiation noise a static assertion deep in a template usually produces.
Two things to keep in mind. Catching by reference is what you want, as always. And this is about constant evaluation, so the exception never exists at runtime. If the same function runs at runtime, it throws in the ordinary way with the ordinary costs.
Constexpr as plain C++
C++26 has quietly finished a long project here. The rule used to be that constexpr code is a restricted dialect you have to write around. It is now close to plain C++ that happens to run early.
Sources: P3068 “Allowing exception throwing in constant-evaluation” · cppreference: constant expressions.