constexprSpecifier Reference (cppreference)- ISO/IEC 14882 C++ Standard Sections:
[expr.const],[except.throw]
constexpr Exceptions refers to the C++26 language capability (standardized via proposal P3032) that permits exception throwing (throw) and exception handling (try-catch) to execute completely within compile-time constant evaluation. Building on C++20's syntactic inclusion of try-catch blocks, C++26 enables exception objects to be thrown, caught, and handled inside the compiler's constant expression interpreter. This allows error-recovery logic, bounds-checked container methods (std::vector::at), and domain validation algorithms to execute at compile time while guaranteeing that uncaught exceptions halt compilation with informative diagnostic error backtraces.
C++20 (via proposal P1002R1) allowed try and catch blocks to appear syntactically inside constexpr functions.
However, C++20 and C++23 suffered from a major operational limitation in constant evaluation:
// C++20/C++23 Constant Evaluation Limitation
constexpr int divide(int a, int b) {
try {
if (b == 0) throw std::invalid_argument("Division by zero!"); // Throws exception
return a / b;
} catch (const std::invalid_argument&) {
return -1; // C++20/23: THIS CATCH BLOCK WAS NEVER EXECUTED IN CONSTEXPR!
}
}
constexpr int res = divide(10, 0); // COMPILE ERROR in C++20/C++23!- Immediate Compilation Failure on
throw: In C++20 and C++23, evaluating athrowstatement during constant evaluation caused compilation to halt immediately, even if thethrowstatement was enclosed inside an activetry-catchblock! The compiler's constant interpreter was incapable of unwinding compile-time stack frames or executingcatchhandlers. - Inability to Test Error Recovery at Compile Time: Standard containers (
std::vector,std::optional) and parsing libraries could not validate exception-recovery logic insidestatic_assertorconstexprunit tests.
Solution: C++26 proposal P3032 updated [expr.const], instructing the compiler's constant expression interpreter to construct exception objects in compile-time memory, unwind compile-time call stacks, and execute matching catch handlers.
When a throw expression is evaluated during constant evaluation:
Execution inside constexpr Function
│
▼
Evaluates 'throw Exception;'
│
┌───────────────────────┴───────────────────────┐
▼ ▼
MATCHING CATCH BLOCK FOUND? NO CATCH BLOCK (UNCAUGHT EXCEPTION)
• Executes catch(const Exception&) block • Constant Evaluation HALTS!
• Resumes normal constexpr execution • Compiler emits diagnostic error
• static_assert / constexpr SUCCEEDS! displaying exception type & message!
- Caught Exception Path: If a matching
catchhandler exists within the constant evaluation call stack, the compiler transfers execution to thecatchblock. Constant evaluation resumes normally and can yield a valid constant expression result. - Uncaught Exception Path: If an exception escapes the outermost constant evaluation boundary without being caught, constant evaluation fails, and the compiler emits a diagnostic build error containing the uncaught exception's type name and message.
┌──────────────────────────────────────────────────────────────────┐
│ C++20 / C++23 │
├──────────────────────────────────────────────────────────────────┤
│ • try-catch blocks permitted syntactically inside constexpr (P1002)│
│ • Evaluating 'throw' caused IMMEDIATE compile-time error │
│ • Catch handlers NEVER executed during constant evaluation │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ C++26 │
├──────────────────────────────────────────────────────────────────┤
│ • Introduced full constexpr Exception Handling (P3032) │
│ • Throwing and catching exceptions supported inside constexpr │
│ • Enables compile-time testing of exception recovery paths │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ C++29 (Candidate / Future) │
├──────────────────────────────────────────────────────────────────┤
│ • Static/constexpr exception optimization refinements │
│ • Extended exception propagation across compile-time reflection │
└──────────────────────────────────────────────────────────────────┘
Demonstrates catching a thrown exception inside a constexpr function during static_assert.
#include <iostream>
#include <stdexcept>
// C++26 constexpr function with fully functional try-catch exception handling!
constexpr int safe_parse_digit(char c) {
try {
if (c < '0' || c > '9') {
throw std::out_of_range("Character is not a decimal digit!"); // Throws exception in constexpr!
}
return c - '0';
} catch (const std::out_of_range&) {
// C++26: Catch handler EXECUTED AT COMPILE TIME!
return -1; // Fallback error value
}
}
int main() {
// Evaluated entirely at COMPILE TIME!
constexpr int digit = safe_parse_digit('7'); // Returns 7
constexpr int error = safe_parse_digit('X'); // Throws out_of_range, caught by catch block -> Returns -1!
static_assert(digit == 7, "Valid digit parsing failed");
static_assert(error == -1, "Compile-time exception catching failed");
std::cout << "Compile-time exception throwing and catching verified successfully.\n";
return 0;
}Demonstrates testing container bounds-checking exceptions during compilation.
#include <iostream>
#include <vector>
#include <stdexcept>
constexpr bool verify_vector_bounds_exception() {
std::vector<int> numbers = {10, 20, 30};
try {
// Access index 5 (Out of bounds!)
int val = numbers.at(5); // std::vector::at throws std::out_of_range!
(void)val;
return false;
} catch (const std::out_of_range&) {
// Caught exception successfully during constexpr evaluation!
return true;
}
}
int main() {
// Verified at COMPILE TIME!
static_assert(verify_vector_bounds_exception() == true,
"Compile-time vector bounds exception test failed!");
std::cout << "Compile-time container exception handling verified.\n";
return 0;
}Demonstrates how an uncaught exception in a constexpr context halts compilation with an informative error message.
#include <iostream>
#include <stdexcept>
constexpr int strict_divide(int a, int b) {
if (b == 0) {
// Uncaught exception during constant evaluation!
throw std::domain_error("Fatal Error: Division by zero in constant expression!");
}
return a / b;
}
int main() {
constexpr int valid_div = strict_divide(10, 2); // OK: Evaluates to 5
// UNCOMMENTING BELOW HALTS COMPILATION WITH DIAGNOSTIC ERROR:
// constexpr int invalid_div = strict_divide(10, 0);
// COMPILER ERROR: Constant evaluation failed!
// Uncaught exception of type 'std::domain_error': "Fatal Error: Division by zero..."
std::cout << "Valid Division Result: " << valid_div << "\n";
return 0;
}-
Use
constexprExceptions for Compile-Time Validation & Testing:- Write
constexprunit tests usingstatic_assertto verify that invalid inputs to your custom container or parser types correctly throw expected exception types.
- Write
-
Uncaught Exceptions Serve as Compile-Time Assertions:
- Intentionally allowing an exception to escape an un-handled
constexprfunction is an effective mechanism for creating custom domain-specific compile-time assertions. The compiler will halt compilation and print the exception message in the build log.
- Intentionally allowing an exception to escape an un-handled
-
Ensure Exception Classes Support Constant Evaluation:
- Custom exception types thrown inside
constexprfunctions must be literal types or satisfyconstexprconstructor requirements.
- Custom exception types thrown inside
- Related to:
- constexpr Specifier — Baseline compile-time function specifier.
- Relaxing Constraints On constexpr Functions — C++14 imperative
constexprrelaxations. - constexpr Virtual Functions — C++20 virtual methods in constant evaluation.
- Immediate Functions (consteval) — Mandatory compile-time functions.
- Superseded by: None.
- Supersedes: C++20's un-catchable
throwrestriction during constant evaluation.
Back to C++26 section.