std is constant evaluated in C++
std::is_constant_evaluated is a function that can be used to determine whether an expression is evaluated at compile time. This function is anticipated to be featured in C++20. It allows the programmer to select between two distinct implementations of a function; the first one is quick for runtime, and the other is compiler-friendly for compile time, which is the primary.
![std::is_constant_evaluated in C++](https://static.tutorialandexample.com/cpp/std-is-constant-evaluated-in-cpp1.png)
The misconception:
A common misperception is that if all of the inputs are known at build time, a constant expression should be evaluated at that point. It used to be difficult to tell, but this is just untrue.
Consider the following:
constexpr int add(int a,int b){
return a+b;
}
int add_test1(){
int r = add(1,2);
return r;
}
Is r computed during compilation? From a conceptual standpoint, the answer is yes, and most compilers would precompute the output if you compiled the code with optimisations enabled.
Instead, the compiler generated the runtime code to call add with arguments of 1 and 2. Subsequently, the optimiser executed, inlining the call and executing continuous folding, ultimately yielding a simple return of 3.
In order to compute r correctly during compilation, you would write.
int add_test2(){
constexpr int r = add(1,2);
return r;
}
std::is_constant_evaluated breaks that assumption:
There wasn't much difference between the two before, but std::is_constant_evaluated makes it possible by allowing you to ask the compiler directly if the call is evaluated at compile time. As a result the following.
int square_test1(){
int r = square(3);
return r;
}
It is necessary to call __magic_fast_square and take the second branch in the square. the optimizer may or may not constantly fold it if it were an intrinsic function. Likewise, the subsequent
Likewise, the code int square_test2(){
constexpr int r = square(3);
return r;
}
Must accept the first branch and be assessed during compilation. This also applies to other scenarios when a constant expression is needed, like calls to construct methods, array limits, template arguments, and case labels.
Determines if the function call takes place in a context that is constantly evaluated. Returns false otherwise; true if the call is evaluated while an expression or conversion that is clearly constant-evaluated is being evaluated. Compilers can initially carry out a trial constant evaluation in order to ascertain whether initializers of the following variables are plainly constant-evaluated:
- variables that are either enumeration, reference, or const-qualified integral types;
- static and thread-local variables.
It is not advised to base decisions in this instance on the outcome.
int y = 0;
const int a = std::is_constant_evaluated() ? y : 1;
// Trial constant evaluation fails. The constant evaluation is discarded.
// Variable a is dynamically initialized with 1
const int b = std::is_constant_evaluated() ? 2 : y;
// Constant evaluation with std::is_constant_evaluated() == true succeeds.
// Variable b was statically initialized 2
Return value:
It returns true if the call is evaluated when evaluating an expression or conversion that is clearly assessed as a constant; otherwise, it returns false.
Possible implementation:
// This implementation requires C++23 if consteval.
constexpr bool is_constant_evaluated() noexcept
{
if consteval
{
return true;
}
else
{
return false;
}
}
Note: std::is_constant_evaluated() always returns true when used directly as the condition of the static_assert declaration or the constexpr if statement.
Compiler extensions are usually used to implement std::is_constant_evaluated if conceal is not present in C++20.
Example:
Run this code
#include <cmath>
#include <iostream>
#include <type_traits>
constexpr double power(double b, int x)
{
if (std::is_constant_evaluated() && !(b == 0.0 && x < 0))
{
if (x == 0)
return 1.0;
double r {1.0};
double p {x > 0 ? b : 1.0 / b};
for (auto u = unsigned(x > 0 ? x : -x); u != 0; u /= 2)
{
if (u & 1)
r *= p;
p *= p;
}
return r;
}
else
{
return std::pow(b, double(x));
}
}
int main()
{
// A constant-expression context
constexpr double kilo = power(10.0, 3);
int n = 3;
// in a constant-expression context
// Equivalent to std::pow(10.0, double(n))
double mucho = power(10.0, n);
std::cout << kilo << " " << mucho << "\n"; // (3)
}
Output:
1000 1000