Designated Initializers, the best feature of C++20
20 points by LesleyLai
20 points by LesleyLai
I've regularly used both C and C++ often, and losing this C99 feature when switching languages was so frustrating. (The C++ -> C equivalent frustration was binary literals from C++14, finally added in C23.) Was there a specific rationale for not having designated initializers for so long? Or was it just that no one had pushed a proposal for it through?
IIRC, the challenges were around its interactions with existing C++ features like constructors and the order that members are constructed. This is also why C++ designated initializers require the initializer elements to be in declaration order: https://old.reddit.com/r/cpp/comments/18onqse/what_is_the_rationale_for_requiring_designated/. It would have been nice if (as one of the commenters in that reddit thread notes) they had relaxed the ordering requirement for types with only trivially constructible/destructible members, which would also ease interop with C libraries whose API ergonomics rely on designated initializers. I guess they could still make that relaxation in a future standard as a non-breaking change.
To be honest, I like that it enforces ordering. It avoids inconsistent ordering, keeping ordering consistent is just an extra cognitive overhead while the freedom to have inconsistent ordering gives me nothing.
The only annoying thing is that Apple Clang doesn't enforce ordering; it doesn't even have an option to enable warnings about breaking C++ ordering rules. So I'll be writing C++ on my Mac, not noticing my inconsistent ordering, then realize my mistake only when I later try to compile the project on my Linux desktop (or when it fails in CI, depending on the project).
The problem is that when using third-party C libraries designed around designated initializers, it's often used in cases where you have a giant pile of fields with 0 as a default-value sentinel. They're not going to order the fields in any particular way in the declaration because C doesn't have that restriction, e.g. they're not going to make it alphabetically ordered to make it easy to remember. So it's just a frustrating experience to interact with from C++ unless your code editor has a one-button quickfix to resolve ordering issues. Which is hopefully a standard feature in newer C++ LSPs and IDEs but I don't recall it being there last time I checked.
Oh, yeah I get that it might be annoying when using designated initializers with structs made for C. I've only happened to use them for C++ structs.
Some notes in this SO answer https://stackoverflow.com/a/29337570/3444805
By 2014 it had already been proposed and rejected several times:
EWG found various problems with the proposed approach, and didn't think it's feasible to try solving the problem, as it has been tried many times and every time it has failed.
We use these a bit in snmalloc to provide API-stable functions with a bunch of optional parameters. The last argument is a struct that has default values for everything and you can use this syntax to override the ones you want to change. This is a C++ localisation of a common Python idiom.
Speaking as someone who doesn't really do C++, that article was not just about designated initialisers, but in the function call instances (from a C perspective) about compound literals as well. That being how one can also provided 'named arguments' in C.
One often underappreciated aspect of C compound literals is that they're not simply restricted to structures but to all (I've not tried all) types. So one can do the following:
int main() {
auto arr = (int[1]){ 1 };
const int a0v = arr[0];
auto larr = (long long[2]){ 5, 7};
float pi = (float){ 3.14 };
printf("arr[0] = %d\n", a0v);
printf("larr[0] = %lld; larr[1] = %lld\n", larr[0], larr[1]);
printf("pi = %f\n", pi);
return 0;
}
While those may look a bit weird, they can come in handy when building certain forms of macro based facility.