C++ 20, designated initializers

https://www.cppstories.com/2022/20-smaller-cpp20-features/#7-designated-initializers

This is one very nice C++ feature, increasing readability. Always a good thing IMO.

A couple of features are nice as well. using enum and range-based for loop with initializer.

Well, all the features are 'nice,' but these 3 stand out for me as something I will use going forward.

Slowly am I getting familiar with what C++20 put in the toolbox.
Last edited on
It's about time C++ added this. It appeared in C99.
Last edited on
Kinda wish it didn't need the dot in front, but I guess it was necessary to prevent breaking existing code.
out-of-order designated initialization, nested designated initialization, mixing of designated initializers and regular initializers, and designated initialization of arrays are all supported in the C programming language, but are not allowed in C++.
https://en.cppreference.com/w/cpp/language/aggregate_initialization
Wonder why they made stricter than C with regard to out-of-order designated initialization. Seems like it would have been an easy win.
To be consistent with list­-initialization, we expect the initializers to be evaluated in left­-to-­right order, as written; but we also want to perform the actual initializations in data members' declaration order, so that they can be destructed in the reverse order. When these two orders do not match, an implementation cannot avoid creating temporaries to fill the gap between the evaluations and initializations.

To meet these expectations for guaranteed copy elision, we require the designators to appear as a subsequence of the data member declaration sequence, so that the evaluation order matches the declaration order, and it is also textually left-­to-­right in designated initialization.
https://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0329r0.pdf

They could have simply copied C#'s solution. In C#,
1
2
3
4
var foo = new Foo{
    Bar = new Bar(),
    Baz = new Baz(),
};
is exactly equivalent to
1
2
3
var foo = new Foo()
foo.Bar = new Bar();
foo.Baz = new Baz();
even if Foo.Foo() initializes Foo.Bar and Foo.Baz to something else. Yeah, it might have required extra constructions, but in this end this is just syntactic sugar. The alternative previously would have been to do
1
2
3
4
Foo temp;
temp.bar = std::move(bar);
temp.baz = std::move(baz);
return temp;
instead of
1
2
3
4
return Foo{
    .bar = std::move(bar),
    .baz = std::move(baz),
};
so I don't see what the big deal is.
I think the dot is necessary to differentiate it from an assignment expression.

Like it or not, the following has always compiled:
1
2
int x = 0;
struct { int x; } obj { x = 5 };
Last edited on
1
2
int x = 0;
struct { int x; } obj { x = 5 };
Is there a name for the concept you just demonstrated here? I see that it's assigning 5 to the outer x, but I can't fathom how it's interpreting that last pair of parentheses before the semi-colon.
Last edited on
> Is there a name for the concept you just demonstrated here?

Aggregate initialization. ( x=5 is an expression of type int )
that last pair of parentheses before the semi-colon
Look more closely. They're braces, not parentheses.
The issue, of course, with in-order designated initialisation is that you need to know the name/ordering specified in the definition. Intellisense (at least in MS) is of no use here as it displays in sorted alpha order - not the object order. If constructor initialisations can be written in any order but evaluated in definition order, then why not designated initialisation be the same...
Look more closely. They're braces, not parentheses.


With C++20, also compiles with parentheses:

1
2
3
4
int x = 0;
struct {
	int x;
} obj ( x = 5 );

Yeah I meant braces. But, okay now I get it. The assignment is the expression itself, and the return value of the assignment is then also initializing the inner x.
Topic archived. No new replies allowed.