Categories
Allgemein

constexpr Variables

constexpr variables are a powerful feature of C++. They have the potential to improve runtime performance and also convert some runtime errors to compile time errors. However, they are sparely used in code bases due to the constraints they impose on the developer. This post will argue that their use, especially in the context of templating should be considered more often.

What is it?

Simply put, a constexpr variable is a variable declared by the developer to be a constexpr. This is done with the constexpr specifier.

constexpr int b = 10;

The semantics of the constexpr specifier are fairly straight-forward.

The constexpr specifier declares that it is possible to evaluate the value of the function or variable at compile time.

cppreference – constexpr specifier

For details consult the cppreference, but for practical use cases mostly one of the following will be true:

  • The variable is assigned a literal like 0 or "Hello World".
  • The variable value is constructed from a (standard) library class that supports constexpr construction (i.e. constexpr std::array<int, 3> myArray{1, 2, 3};).
  • The variable holds a struct or class that is constructable through its default constructor.

Note that there are also ways to generate constexpr values through functions (including constructors), but this is not the focus of this post.

constexprs may be evaluated at compile time, but are not guaranteed to be so. However, they may be used in cases, where a value must be known at compile time. This is mostly in cases, where the memory layout of something must be determined, like in template instantiations or array sizing.

Constexpr and Templates

In addition to type parameters templates may also take so-called “non-type parameters”, which must be known at compile time. This effectively makes it so that these parameters must be constant expressions e.g. a constexpr variable or an integral type. These can be used inside the template in constant expressions as well. This means that i.e. a fixed-size array can be allocated with this parameter. In fact allocating some type of array or other memory structure using a size_t template parameter is the use I observed most commonly.

Advantages

constexpr variables mostly perform better in two departments: performance and error handling

Some of the performance improvements are actually dependent on the compiler. Compilers may use the (compile time) knowledge that a condition is always true to remove unused branches and perform the evaluation of the branch condition at compile time. This is mostly relevant in the context of templating, where a template might account for different template parameters. Fixed size data structures are also the only ones allocatable on the stack (opposed to the heap). So there can also be performance improvements, when otherwise dynamic size structures can be allocated en bloc on the stack instead of being dynamically allocated on the heap.

The compile-time knowledge of the value of a variable can also be useful for the compiler or linter to issue warnings (or errors) at compile time. I.e. a constexpr variable whose value is 0 should not be divided by or constexpr integer variables whose values are negative should not be used to construct arrays. To illustrate, the following code does not compile:

constexpr int n = -10;
int badArray[n];
// error: size of array 'badArray' is negative

In this case this does not seem especially impressive, because the same error would occur if the -10 would just be used directly as the array size. This should be considered more in the context of a regular class trying to allocate its memory at runtime to hold all values and a template class that has a member array of fixed size. The template would find the error at compile time, which allows the programmer to fix the issue immediately.

Last but not least constexpr also implies const so the value of the variable may not change during execution (if nobody is const-casting).

Note that these compile-time calculations might increase compile time (significantly in some cases), but that this time is usually shaved off at runtime.

Conclusion

There are very valid use cases to use constexpr variables, especially in the context of templating. The initial burden of figuring out what can actually be used as a constexpr value are often compensated by improvements in error handling and performance. In conclusion, I would recommend their use more often.

By Tilmann Matthaei

I'm an aspiring software professional looking to share what I learn about reliable software along the way.

I hold a Bachelor's Degree in Applied Computer Science - Digital Media and Games Development and am working in software development since 2018.
I have experience in embedded development (mostly in C++) as well as Continuous Integration and IT Security.

Feel free to contact me via tilmann@matthaei.dev.