C++20: “constinit” Specifier

Written by on October 15, 2023 in C++, Programming with 0 Comments

The constinit specifier, introduced in C++20, is applied to static variables (global and local static) and thread local variables, with the requirement that they either have a zero initialization or they are initialized with a compile-time constant expression.

Here is our first example:

Example-1: Basic Types

Example-1: Basic Types

Line 17 declares a global constinit variable, initialized with a constant value. Line 18 is an example of zero initialization.

Line 27 (commented out) shows that it is illegal to declare a local constinit variable. However, we can have local static variables that are constinit qualified. This is shown in Line 28.

Notice that constinit variables need not be const  (immutable) throughout their lifetime. They can be modified at runtime. This is shown in line 32, where “global2” is assigned a value computed from a regular function call. This is perfectly legal.

Here is the program output:

Program Output

Program Output

No surprise here.

The next example is based on an aggregate data type, a struct in this case:

Example-2: Aggregate Type

Example-2: Aggregate Type

For the sake of illustration, I have defined three constructors, the third one being constexpr qualified. Just as in the case of a variable of a primitive type, an object of “Temp” struct can be declared using constinit at “static” scope, but only when the “appropriate” constructor is available. As lines 48-50 show, the constexpr constructor is the only one that can be used here.

Line 54 is a reminder that local variables (even using constexpr constructor) cannot be qualified constinit. As in the earlier example, in line 55 we modify the state of the constinit qualified variable at runtime. This is what the program prints:

Program Output

Program Output

Our final example shows the use of constinit applied to “thread local” variables:

Example-: Thread Local Variables

Example-3: Thread Local Variables

Here we have a global thread_local variable as well as a local static (declared inside thread_fn()), both of them declared with constinit qualifier. Each instance of thread_fn() increments these two variables and prints on standard output. The mutex is used to regulate access to “std::cout” instance.

Here is the output I get when I execute the program:

Multithreading Output

Multithreading Output

I could have added more threads to make the behavior more interesting, but I hope you get the idea.

One last thing before I conclude this article. constinit variables defined across multiple source files can be declared “extern” where needed, for example:

extern constinit int global1;

Since the variable is assumed to have been defined in some module, initialization is not required here.

I tested this code in Visual Studio Professional 2022 (64 it) Ver 17.7.5. You can download the source file here.

Have a great weekend!

Tags: ,

Subscribe

If you enjoyed this article, subscribe now to receive more just like it.

Subscribe via RSS Feed

Leave a Reply

Your email address will not be published. Required fields are marked *

Top