consteval void process() {
constexpr std::array a1{0, 8, 15};
constexpr auto n1 = std::ranges::count(a1, 0); // OK
std::array<int, n1> a1b; // OK
std::array a2{0, 8, 15};
// constexpr auto n2 = std::ranges::count(a2, 0); // ERROR
std::array a3{0, 8, 15};
auto n3 = std::ranges::count(a3, 0);
// std::array<int, n3> a3b; // ERROR
}
std::is_constant_evaluated
Yields true when it called in a manifestly constant-evaluated expression or conversion.
That is roughly the case if we call it:
- in a constant-expression, or
- in a constant context (in 'if constexpr', a 'consteval function', or an 'constant initialization')
- for an initializer of a variable usable at compile time
#include <type_traits>
#include <cstring>
constexpr int len(const char* s) {
if (std::is_constant_evaluated()) {
// compile-time friendly code
int idx = 0;
while (s[idx] != '\0') {
++idx;
}
return idx;
} else {
return std::strlen(s); // function called at runtime
}
}
constexpr bool isConstEval() {
return std::is_constant_evaluated();
}
bool g1 = isConstEval(); // true
const bool g2 = isConstEval(); // true
static bool g3 = isConstEval(); // true
static int g4 = g1 + isConstEval(); // false
int main() {
bool l1 = isConstEval(); // false
const bool l2 = isConstEval(); // true
static bool l3 = isConstEval(); // true
int l4 = g1 + isConstEval(); // false
const int l5 = g1 + isConstEval(); // false
static int l6 = g1 + isConstEval(); // false
int l7 = isConstEval() + isConstEval(); // false
const auto l8 = isConstEval() + 42 + isConstEval(); // true
}
bool runtimeFunc() {
return std::is_constant_evaluated(); // always false
}
constexpr bool constexprFunc() {
return std::is_constant_evaluated(); // may be false or true
}
consteval bool constevalFunc() {
return std::is_constant_evaluated(); // always true
}
void foo() {
bool b1 = runtimeFunc(); // F
bool b2 = constexprFunc(); // F
bool b3 = constevalFunc(); // T
static bool sb1 = runtimeFunc(); // F
static bool sb2 = constexprFunc(); // T
static bool ab3 = constevalFunc(); // T
const bool cb1 = runtimeFunc(); // F
const bool cb2 = constexprFunc(); // T
const bool cb3 = constevalFunc(); // T
int y = 42;
static bool sb4 = y + runtimeFunc(); // F
static bool sb5 = y + constexprFunc(); // F
static bool ab6 = y + constevalFunc(); // T
const bool cb4 = y + runtimeFunc(); // F
const bool cb5 = y + constexprFunc(); // F
const bool cb6 = y + constevalFunc(); // T
}
When it makes no sense to use std::is_constant_evaluated()
As condition in a compile-time if, because that always yields true:
if constexpr (std::is_constant_evaluated()) {
// always true
}
Inside a pure runtime function, because that usually yields false.
The only exception is if it is used in a local constant evaluation:
void foo() {
if (std::is_constant_evaluated()) {
// always false
}
const bool b = std::is_constant_evaluated(); // true
}
Inside a consteval function, because that always yields true:
consteval void foo() {
if (std::is_constant_evaluated()) { // always true
}
}
Therefore, using std::is_constant_evaluated() usually only makes sense in constexpr functions.
std::is_constant_evaluated() and Operator ?: (conditional (ternary) operator)
int sz = 10;
constexpr bool sz1 = std::is_constant_evaluated() ? 20 : sz; // true, so 20
constexpr bool sz2 = std::is_constant_evaluated() ? sz : 20; // false, so ERROR
Reasoning as follows:
- The initialization of sz1 and sz2 is either static initialization or dynamic initialization.
- For static initialization, the initializer must be constant. So, the compiler attempts to evaluate the initializer with std::is_constant_evaluated() treated as a constant of value true.
- With sz1, that succeeds. The result is 1, and that is a constant. So, sz1 is constant initialized with 20.
- With sz2, the result is sz, which is not a constant.
So, sz2 is (notionally) dynamically initialized. - Please refer to https://vsdmars.blogspot.com/2022/02/kernnelcc-ternary-conditional-operator.html as well as [ms doc] C++ Conditional Operator: ?:
- Rule of thumb; for ternary operator; both operands should have same type/value category; otherwise a conversion shall be triggered.
- Therefore, the previous result is discarded and the initializer is evaluated with std::is_constant_evaluated() producing false instead. So, the expression to initialize sz2 is also 20.
- However, sz2 is not necessarily a constant, because std::is_constant_evaluated() is not necessarily a constant-expression during this evaluation. So, the initialization of sz2 with this 20 does not compile.
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.