With C++11, compile-time programming has flourished in C++, and ever since, new standards kept on pushing new ideas to provide a richer and richer compile-time programming toolbelt. The upcoming release of the next standard, C++20, is no exception to this rule, and will provide its share of new features regarding this domain.
One of the accepted proposals for C++20 introduces immediate functions, that is, functions that are always evaluated at compile-time. This post will present these, by highlighting their differences with
With the introduction of
constexpr, compile-time evaluation of functions transitioned from being an uncertain compiler optimization (depending on the code, the compiler, the optimization level, the current position of the stars, etc) to being a standardized language feature. Since this post is about functions, we will only discuss
constexpr functions here.
As a language feature,
constexpr functions provide more guarantees than regular functions that might or might not be candidates for constant propagation. For example, the compiler will verify that a function marked as
constexpr can actually be reduced to a constant expression, and thus evaluated at compile-time. This has the useful consequence of making
constexpr functions usable anywhere a constant expression is required.
However, it is not guaranteed that a
constexpr function will always be evaluated at compile-time. This happens, for example, if it is passed arguments that are not constant expressions themselves (godbolt link) :
constexpr int twice(int i)
return i * 2;
constexpr int four = twice(2); // OK, evaluated at compile-time
int three = 3;
int six = twice(three); // OK, but not evaluated at compile-time because "three" is not a constant expression
Furthermore, it is actually not even guaranteed that the compiler will “try its best” to evaluate it at compile-time (see this example).
To sum up,
constexpr is only a marker used to express that the function can be used in a constant expression if needed.
Getting more specific with
As we said above, the
constexpr marker does not require the function to always be evaluated at compile-time. This can be useful in some contexts : for example, we want to be able to use STL algorithms at compile-time if needed, but also at runtime.
However, in some other contexts, we want to require compile-time evaluation for a function, and emit a compiler error if that is not possible. This was the topic of the P1073 proposal, which introduces the
consteval specifier for functions. Its syntax is rather unsurprising:
consteval int twice(int i)
return i * 2;
constexpr int four = twice(2); // OK
int three = 3;
// int six = twice(three); // KO, "three" is not a constant expression
It is also important to note that since they are evaluated at compile-time only,
consteval functions will never be seen any further than the compilation step. No symbol will ever be emitted for them, thus their address cannot be taken, and they won’t show up in a debugger.
Why is this useful ?
You might be wondering why restricting the evaluation of certain functions to compile-time only is useful. After all, why forbid evaluation at runtime ? In order to understand the benefits of this feature, it is important to highlight its context as part of the language evolution.
- The C++ committee has plans to make compile-time programming even more powerful in the upcoming years. There are lots of ideas and proposals out there, among which are the reflection and the metaclasses debates. For obvious reasons, these two features will live entirely in the compile-time world, thus they represent very important use cases for the
- Another place where
constevalfunctions could prove useful would be taking over yet another use case of the preprocessor: function-like guaranteed compile-time calculations, which do not generate any code.
- Furthermore, as we said above,
constexprfunctions can be evaluated at both compile-time and runtime. With the increasing number of library functions becoming
constexprand the ability to detect constant evaluation (with
constexprfunctions can be used efficiently both at runtime and compile-time. On the other hand,
constevalfunctions should take over compile-time-only helpers.
consteval proposal was accepted into C++20, it should be part of the next batch of new features to appear in the C++ language, and will be ready to support other upcoming proposals for increasingly powerful compile-time programming.