Let's try C++20 | std::is_constant_evaluated()
Tags: cpp, programming, todayilearned
Published: 2020-12-28 16:35:02

`std::is_constant_evaluated()` is one the many new small features of C++20. [Specified in `P0595R2`](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0595r2.html), this function is available in the `<type_traits>` header. Here is its description from [cppreference](https://en.cppreference.com/w/cpp/types/is_constant_evaluated):

>`constexpr bool is_constant_evaluated() noexcept;`
>
>Detects whether the function call occurs within a constant-evaluated context. Returns `true` if the evaluation of the call occurs within the evaluation of an expression or conversion that is manifestly constant-evaluated; otherwise returns `false`.

This sounds pretty straight-forward.

## Benefits for `constexpr` functions

This may be very interesting for `constexpr` functions. You probably know that you have no guarantee that a `constexpr` function is really executed at compile-time. This depends on how the function is called:

```cpp
constexpr int foo() {
    return 0;
}
    
constexpr auto a = foo(); // compile-time executed
auto b = foo(); // runtime executed (*)
```

(*) : in fact, there is no guarantee the function is evaluated at runtime. Based on the content of the function, the scope of the variable, on the optimization level, or other factors, the compiler may evaluate `b` at compile-time.

Sometimes, you may want to behave differently whether the function is compile-time or runtime executed. With `std::is_constant_evaluated()`, it is now possible:

```cpp
constexpr int foo() {
    if (std::is_constant_evaluated()) {
        return 0;
    } else {
        std::cout << "at runtime\n";
        return 1;
    }
}
```

Based on the context, `foo()` will return either 0 or 1. At mentioned above, you may be surprised. For instance, with clang 11.0.0, the following code returns 1:

```cpp
int a = foo();

int main() {
    int b = foo();
    return a + b;
}
```

Indeed, `a` is constant-evaluated. Apparently, it is because `a` has a static storage duration (while `b` has automatic storage duration) and this ["is another context that might be evaluated at compile time [...] and this too interacts with std::is_constant_evaluated."](https://gist.github.com/Som1Lse/5309b114accc086d24b842fd803ba9d2#interaction-with-static-initialisation)

## In non-`constexpr` functions

It turns out that the usage of `std::is_constant_evaluated()` is relevant only in `constexpr` function. See this discussion on stackoverlow: ["C++20 | std::is_constant_evaluated() and const variables"](https://stackoverflow.com/questions/65429218/c20-stdis-constant-evaluated-and-const-variables).

For instance, it is pointless to use it in a regular function to initialize a `const` variable (which is never considered as a constant-evaluated context, see "call (3)" in P0595R2), or in a`consteval` function (which is for sure called in constant-evaluated context).

Note that the proposal states:

>The standard doesn't actually make a distinction between "compile time" and "run time", and hence a more careful specification is needed, one that fits the standard framework of "constant expressions".
>
>Our approach is to precisely identify a set of expressions that are "manifestly constant-evaluated" (a new technical phrase) and specify that our new function returns true during the evaluation of such expressions and false otherwise.

This is why cppreference uses "constant-evaluated context" and not "compile-time context".

## Warnings in obvious cases

According to cppreference:

>When directly used as the condition of `static_assert` declaration or `constexpr if` statement, std::is_constant_evaluated()` always returns `true`.

You will probably get a warning from your compiler in such cases.

With clang:

```bash
warning: 'std::is_constant_evaluated' will always evaluate to 'true' in a manifestly constant-evaluated expression [-Wconstant-evaluated]
```

With gcc (if you add the option `-Wall`):

```cpp
warning: 'std::is_constant_evaluated' always evaluates to true in 'if constexpr' [-Wtautological-compare]
```

## Conclusion

`std::is_constant_evaluated()` is a great way to select a compile-time friendly algorithm in `constexpr` function. Stay the safe edge of sanity and don't use it to completely change the behavior of a function as I did in my examples 😉