constexpr v1: single-return functions only — compile-time constants without macros. const methods define logical constness; mutable covers caches.
constexpr int sq(int x) { return x * x; }
struct Table {
int lookup(Key k) const;
mutable std::mutex m_;
};
Relaxed constexpr: loops, locals, mutation. Compile-time code starts looking like normal code.
constexpr int pop(unsigned v) {
int n = 0;
for (; v; v >>= 1) n += v & 1;
return n;
}
if constexpr brings compile-time branching into regular functions; lambdas are implicitly constexpr where possible.
The big leap: consteval (must run at compile time), constinit (compile-time init, runtime mutable), constexpr virtuals, try, dynamic allocation — std::vector and std::string work in constant evaluation.
consteval int parse_version(std::string_view s);
constinit std::atomic<int> counter{0};
constexpr auto sorted = [] {
std::array a{3, 1, 2};
std::ranges::sort(a);
return a;
}();
if consteval replaces std::is_constant_evaluated() (and can call consteval functions); constexpr unique_ptr; static constexpr allowed inside constexpr functions.
constexpr int fast_log2(unsigned v) {
if consteval { return portable_log2(v); }
else { return std::countl_zero(v) ^ 31; }
}
Constant evaluation approaches completeness: static_cast from void*, placement new, and thrown-and-caught exceptions all work at compile time — and reflection is itself a consteval library.
constexpr int checked(int x) {
try { return risky(x); }
catch (const std::exception&) { return -1; }
}
C++29direction · not adopted
Direction: the remaining gaps (more of the library marked constexpr, compile-time I/O-adjacent facilities) keep shrinking release by release.