Wrocław expands constant evaluation while contracts remain under review
The November meeting adopts exception handling during constant evaluation. Reflection and contracts continue through specification review, with different levels of completion that matter when describing C++26.
A constexpr function can use the same algorithm as its runtime counterpart and still need different error handling if a reached throw expression ends constant evaluation. The November C++ meeting removes part of that difference. Herb Sutter's report, published on 24 November, identifies adoption of P3068R6, allowing exceptions during constant evaluation. The same report places reflection in wording review and contracts in continuing review. Those are different stages of work, even though all concern the developing C++26 revision.
C++'s ability to express abstractions without a runtime layer is useful to me. Maintaining another version of an algorithm solely for translation-time restrictions is less appealing. Allowing ordinary error-handling structure during constant evaluation can reduce that duplication, provided the boundary on a completed evaluation remains clear.
Catching an exception can produce a valid compile-time result
Hana Dusíková's P3068R6, dated 19 November, permits throwing and handling an exception within constant evaluation. The exception cannot escape the evaluation as a surviving runtime object. An uncaught exception still prevents the required constant expression from succeeding. The addition permits recovery within the evaluation, without making every error recoverable.
This changes what a compile-time adapter can do. A function can invoke an operation that reports a selected failure by throwing, catch that failure and produce another result. Previously, reaching the throw itself blocked that computation. The revised model can distinguish a handled exceptional path from an uncaught one.
The following example illustrates the adopted direction. It uses a small user-defined exception type, keeping standard-library exception availability out of the example.
struct invalid_digit {};
constexpr int digit(char c)
{
if (c < '0' || c > '9')
throw invalid_digit{};
return c - '0';
}
constexpr int digit_or(char c, int fallback)
{
try {
return digit(c);
} catch (invalid_digit) {
return fallback;
}
}
static_assert(digit_or('x', 4) == 4);
The handler supplies a successful result for the outer evaluation. It does not redefine the digit parser's valid input domain. A direct required constant evaluation of digit on the same invalid input still has an uncaught exception. The distinction follows the error-handling structure written in the program.
Undefined behavior does not become a catchable exception
The paper explicitly preserves other constant-evaluation failures. Its example of dereferencing a null pointer inside try remains invalid. No exception has been thrown for a handler to catch. Adding exception support does not turn the constant evaluator into a general recovery mechanism for invalid language operations.
This is an important limit for library design. A recoverable domain error needs an operation that actually reports that error through the selected mechanism. An invalid memory access has a different status. A catch-all handler cannot establish that arbitrary code inside the protected block will produce a valid constant expression.
The same principle applies at runtime, but the distinction is particularly visible in compile-time claims. A library advertising constexpr support needs to describe which input failures it reports and which operations its valid paths use. Exception support removes one restriction, without validating an algorithm whose other operations violate the evaluation rules.
The exception object's lifetime stays inside the evaluation
P3068R6 treats the exception object's lifetime as part of the containing constant evaluation. Objects and relevant copies created through exception propagation must be destroyed before that evaluation completes. A program cannot use exception_ptr to retain a compile-time exception for later runtime access.
That resembles the transient-allocation boundary discussed in P0784R7. A computation can use temporary resources while it runs, then produce a result that does not depend on keeping those resources alive. The ability to use the mechanism internally is distinct from promoting its internal object into the runtime program.
For an application, the useful output is therefore a completed result of recovery. The handler might choose a fallback or translate the failure into a value. It cannot simply preserve the original exception object beyond the permitted lifetime. That requirement helps keep the boundary between translation-time work and runtime state explicit.
I favor this boundary because it gives the shared algorithm a comprehensible ownership story. Temporary error state can exist while the computation decides its result. The final result must have a representation that remains valid independently of that temporary state.
Other large proposals have different status
The meeting report describes reflection as remaining in specification wording review. It also reports that language evolution forwarded contracts to Core for wording work while contentious design points remained. These are advances toward possible C++26 inclusion, rather than announcements that either facility has completed the adoption process.
The distinction matters when reading demonstrations. A proposal can have implementation experiments and a well-developed design while its final wording is still changing. A project can evaluate those experiments without treating their current syntax and behavior as a published standard interface. The report's separate status descriptions are useful evidence for that decision.
Reflection had already reached wording review in Sutter's July report. November reports continued review. This is a continuation of the specification work, with exception handling supplying a concrete newly adopted facility relevant to compile-time programming.
Library exception types need their own support
The paper also contains library wording for exception propagation and selected standard exception classes. It leaves changes to other exception types to separate papers. This is another reason to distinguish a language capability from availability across a particular library implementation. Accepting a throw expression during constant evaluation does not make every existing exception constructor suitable for that evaluation.
A project using a library-specific exception should therefore follow the constructor and handler path it actually invokes. The small example avoids that dependency by using an empty literal type. A larger example could require allocation or message storage during construction, making the payload's lifetime another part of the analysis. The adopted language mechanism supplies the control flow, while the exception type supplies the state carried through it.
For library authors, the immediate result is a more reusable error-handling path within constant evaluation. A caught exception can contribute to a successful result, while uncaught exceptions and unrelated invalid operations retain their failure status. That gives proposed compile-time facilities a familiar mechanism for reporting recoverable errors without allowing temporary exception state to escape into runtime.