C++ has been steadily adding features to let programmers distinguish code that runs at compile time from code that runs at runtime. Two important tools for this are the function std::is_constant_evaluated() (C++20) and the language-level if consteval (C++23). This post explains both, shows practical examples, compares their guarantees and trade-offs, and suggests when to use each.
Both techniques let you write branches that behave differently depending on whether evaluation happens in a constant-evaluation (compile-time) context or at runtime. The differences are subtle but important: one is a function returning a boolean, the other is a special if that the compiler treats as a compile-time-only branch check.
std::is_constant_evaluated() (C++20)
This is a function declared in <type_traits>:
#include <type_traits>
constexpr bool std::is_constant_evaluated() noexcept;
It returns true when the current expression is being evaluated in a constant-expr (compile-time) context, and false otherwise.
Example:
#include <iostream>
#include <type_traits>
constexpr int f() {
if (std::is_constant_evaluated()) {
return 42; // compile-time
} else {
return 0; // runtime
}
}
int main() {
constexpr int a = f(); // evaluated at compile time -> 42
int b = f(); // evaluated at runtime -> 0
std::cout << a << ", " << b << "\n"; // prints "42, 0"
}
if consteval (C++23)
if consteval is a language-level if that the compiler uses to determine whether the current evaluation context is a constant evaluation. It provides stronger compile-time guarantees: the compiler knows the consteval branch must be usable in constant evaluation and will reject code that cannot appear in that context.
Syntax:
if consteval {
// compile-time-only code
} else {
// runtime-only code
}
Example:
#include <iostream>
constexpr int f() {
if consteval {
return 42; // compile-time version
} else {
return 0; // runtime version
}
}
int main() {
constexpr int a = f(); // compile-time context -> returns 42
int b = f(); // runtime context -> returns 0
std::cout << a << ", " << b << "\n"; // prints "42, 0"
}
Key differences (summary)
- Form: std::is_constant_evaluated() is a function; if consteval is a language-level conditional.
- Standard: std::is_constant_evaluated() arrived in C++20; if consteval arrived in C++23.
- Guarantees: if consteval gives the compiler compile-time guarantees and rejects code in the consteval branch that cannot be used at compile time. std::is_constant_evaluated() is more permissive: it returns a boolean but does not force the compiler to reject invalid compile-time-only constructs in the other branch.
- Use inside expressions: std::is_constant_evaluated() can be used inside expressions where a statement-form if cannot (e.g., inside a ternary operator). if consteval requires a statement-level context.
Concrete comparison example
Two functions that appear similar but behave differently in terms of enforcement:
// if-consteval example
constexpr int f() {
if consteval {
return 1; // compile-time only
} else {
return 2; // runtime only
}
}
// std::is_constant_evaluated() example
#include <type_traits>
constexpr int g() {
if (std::is_constant_evaluated()) {
return 1; // may still compile runtime path
} else {
return 2;
}
}
A scenario where the difference matters:
// With if consteval the compiler will reject invalid compile-time-only constructs
constexpr int bad() {
if consteval {
std::cout << "compile-time"; // ❌ error — I/O is not allowed in constant evaluation
}
return 0;
}
// With std::is_constant_evaluated(), the compiler is more permissive and may compile until the code
// is actually used in a constant expression context.
#include <type_traits>
constexpr int maybe_bad() {
if (std::is_constant_evaluated()) {
std::cout << "compile-time"; // may compile; error surfaces only when evaluated in a const-expr
}
return 0;
}
When to use which
- If you target C++23 (or later) and want clear, compile-time-enforced branching: prefer
if consteval. - If you must support only C++20 environments: use
std::is_constant_evaluated(). - If you need the check within an expression (not a statement), use
std::is_constant_evaluated()becauseif constevalis statement-level. - If you want the compiler to reject code that cannot be used at compile time,
if constevalprovides stronger guarantees.
Practical use cases
- Write different implementations for compile-time vs runtime (fast precomputation vs slower runtime logic).
- Guard runtime-only operations (like I/O, dynamic allocations, or system calls) so they are not accidentally used in a constant-evaluation path.
- Provide clearer, intention-revealing code when authoring libraries that may be used in both constexpr contexts and normal runtime contexts.
Short, practical checklist
- Need expression-level check? →
std::is_constant_evaluated(). - Want compiler-enforced compile-time-only branch? →
if consteval. - Targeting C++20 only? →
std::is_constant_evaluated(). - Targeting C++23+ and prefer clarity & safety? →
if consteval.
TL;DR
std::is_constant_evaluated() — C++20: function that returns true inside compile-time evaluation (good for expression-level checks).
if consteval — C++23:
- language-level conditional that the compiler treats as a compile-time-only branch;
- stronger compile-time enforcement and clearer intent.
Closing notes
Both tools are useful. If you can use C++23, prefer if consteval for clearer semantics and stronger compile-time guarantees, and fall back to std::is_constant_evaluated() for compatibility with C++20 or when you need expression-level checks.
C/C++ Programming
- Understanding std::transform_reduce in Modern C++
- Implement a Lock Acquire and Release in C++
- Detecting Compile-time vs Runtime in C++: if consteval vs std::is_constant_evaluated()
- C++ Forward References: The Key to Perfect Forwarding
- Understanding dynamic_cast in C++: Safe Downcasting Explained
- C vs C++: Understanding the restrict Keyword and its Role in Optimization
- C++ Lvalue, Rvalue and Rvalue References
- C++ assert vs static_assert
- Why auto_ptr is Deprecated in C++?
- C++ What is the consteval? How is it different to const and constexpr?
- Tutorial on C++ std::move (Transfer Ownership)
- const vs constexpr in C++
- Tutorial on C++ Ranges
- Tutorial on C++ Smart Pointers
- Tutorial on C++ Future, Async and Promise
- The Memory Manager in C/C++: Heap vs Stack
- The String Memory Comparision Function memcmp() in C/C++
- Modern C++ Language Features
- Comparisions of push_back() and emplace_back() in C++ std::vector
- C++ Coding Reference: is_sorted_until() and is_sorted()
- C++ Coding Reference: iota() Setting Incrementing Values to Arrays or Vectors
- C++ Coding Reference: next_permutation() and prev_permutation()
- C++ Coding Reference: count() and count_if()
- C++ Code Reference: std::accumulate() and Examples
- C++ Coding Reference: sort() and stable_sort()
- The Next Permutation Algorithm in C++ std::next_permutation()
–EOF (The Ultimate Computing & Technology Blog) —
1056 wordsLast Post: Teaching Kids Programming - Two Ways to Prove Square Root of Two is Irrational (proof by contradiction and geometric infinite descent)
Next Post: Alpha Arena: How AI Performs in the Real Crypto Market