Designated Initializers in C++ 20

C++ supports direct assignment initialization for aggregate types, in the form of:

T object = { arg1, arg2, ... };

Starting from C++11, list initialization was introduced, which is also applicable to aggregate types:

T object { arg1, arg2, ... };

C++20 introduces a new form of initialization for aggregate types, known as designated initialization (in fact, C11 already supports designated initialization), which is structured as follows:

T object = { .designator1 = arg1 , .designator2 { arg2 } ... };T object { .designator1 = arg1 , .designator2 { arg2 } ... };

Let’s look at an example of designated initialization:

struct A { int x; int y; int z; }; // Designated initialization, a.y is default initialized to 0A a{.x = 1, .z = 2}; // or A a{.x{1}, .z{2}}; // Traditional list initialization requires specifying each value in orderA b{1, 0, 2};

The most intuitive aspect of designated initialization is that you can specify member names. Compared to list initialization, designated initialization is more targeted, and members that are not explicitly named are automatically default initialized.

Now let’s discuss the specific syntax rules. Although designated initialization allows for explicit naming, it must be for non-static data members and must follow the order of member declarations (C11 does not have this requirement). Similarly, narrowing conversions are also not allowed. For example, in the previous example of A’s initialization, the following would be incorrect:

A a{.y = 2, .x = 1}; // Error, not in orderA b{.x = 1, .z = 23.5 }; // Error, narrowing conversion is prohibited

Nesting designators is also not allowed, for example:

struct A { int x, y; };struct B { A a; double f; };B b = { .a.x = 0 }; // Error, nested names are not supportedB b = { .a = {0, 1} }; // OK, this is allowed

For unions that qualify as aggregate types, designated initialization also has requirements, namely that only one member can be initialized, which is referred to as the active member, for example:

union U { int a; const char* b; };U f = {.b = "asdf"}; // OK, now the active member is bU g = {.a = 1, .b = "asdf"}; // Error, can only specify one member for initialization

Finally, it should be noted that although designated initialization was introduced in C++20, many compilers had already supported the syntax feature of designated initialization prior to this. The reason is that the C language standard has supported designated initialization since C11, and some C++ compilers began supporting designated initialization early to accommodate newer C code. However, it is important to note that their syntax requirements may differ slightly, for example, C’s designated initialization does not require names to be specified in declaration order, etc.

Designated initialization can bring better readability and flexibility to code. For instance, one implementation of the Named Parameter Idiom utilizes the syntax of designated initialization.

References

[1] https://en.cppreference.com/w/cpp/language/aggregate_initialization

Information, code, and past articlesCovariant return types in C++Size and ssize functions in C++Latch and barrier in C++Semaphore in C++lock() and try_lock() functions in C++Various locks in C++Various std::mutex in C++span in C++Copy elision in C++C++ time library part eight: format and formattingPI in C++20[[noreturn]] specifier in C++How to declare lambda as a friend in C++Cache line interface in C++clamp function in C++Three and five rules in C++Strict Aliasing rules in C++construct_at function in C++Content and notification summary

Leave a Comment