6 ms·
> the main caveat regarding designated initializer usage is that initialized members must appear in declaration order I hate this about C++! In C you can init
by rwmj 3y ago
> the main caveat regarding designated initializer usage is that initialized members must appear in declaration order
I hate this about C++! In C you can initialize them in any order, and this allowed us to write nbdkit plugins in a very natural way:
static struct nbdkit_plugin plugin = {
.name = "myplugin",
.open = myplugin_open,
.get_size = myplugin_get_size,
.pread = myplugin_pread,
.pwrite = myplugin_pwrite,
/* etc */
};
where the order is not related to the order the fields appear in the struct (that has to be maintained for ABI reasons), and not all fields need to appear (the others are initialized with 0/NULL).
For C++ we have to do this mess:
https://bugzilla.redhat.com/show_bug.cgi?id=1418328#c3 https://bugzilla.redhat.com/show_bug.cgi?id=1418328#c3
Anyway my question is .. why is this, C++ people?
- zalyalov 3y agostruct S { int A; int B = A; int C; }; int i = 0; S s = {.C = i++, .A = ++i}; What would you expect code like this to do?
- rwmj 3y agoI wouldn't write that, and if someone did they'd get to keep all the pieces.
- flohofwoe 3y agoThe only useful solution for C++ would be to let the compiler reorder the initialization in declaration order, e.g. first A, then B, then C. This would still be a lot better for 99% of real world use cases than requiring the programmer to manually place the items in declaration order.
- IshKebab 3y agoThis would be a big footgun though. That already happens for initialising class members in constructors and it's enough of a footgun that compilers have a warning to tell you if your initialisation order is different to the declaration order.
- jzwinck 3y agoDoesn't the same footgun apply to function call arguments which are evaluated in an unspecified order? f(a++, b++) That footgun doesn't seem to have very much impact in the real world that I've seen. Largely because people do not write complex expressions like that anymore. Since we already mostly avoid such expressions, we may as well take the benefit for designated initializers.
- IshKebab 3y agoYeah that is a footgun too. Enough that Rust defines argument evaluation order. It is a much smaller footgun though and I don't recall ever being bitten by it. I have definitely been bitten by member initialisation order though multiple times. Generally because of questionable designs where one member is passed as a parameter of another member and it hasn't actually been initialised yet. Really I think the answer is to initialise in the order that initialisation is written (this is what Rust does) rather than declaration order. But that would be a breaking change so I guess they opted for the conservative choice.
- gpderetta 3y agoYes, and there was a strong push to define the order although it ultimately failed. On the other hand evaluation order of aggregate initializers is well defined (left to right) and there was little appetite to weaken it. It might still happen in the future of course.
- calamari4065 3y agoIs `int B = A` even legal? Aren't field initializers like that supposed to be compile time constant?
- nemetroid 3y agoIt's allowed since C++11.
- CamperBob2 3y agoFail to compile due to forward references in initializers. The problem is, someone thought this was a good idea, but the act of supporting it ruled out a lot of more-useful future improvements.
- wheybags 3y agoDestructors will execute in the reverse of declaration order, so if initialization order doesnt match declaration order, and some members depend on each other somehow, things will break. At the very least, it could be surprising. Not a problem in C where destructors don't exist.
- sp1rit 3y agoIf you can use GCC to target all platforms, you can build with -std=gnu++XX to use C-like initializers.
- flohofwoe 3y agoIt's not just the random initialization order that didn't make it from C99 into C++, other features also don't work, most notably: ...no chaining of designated initializers: const bla_t bla = { .a.b.c = 23 }; ...and no array indexing: const blub_t blub = { .arr = { [4] = 23, [2] = 1 } }; ...all those limitations taken together, and the C++ designated initialization feature is pretty much useless except for the most trivial structs - while in C99, designated initialization really shines with complex, nested structs. The funny thing is that none of those limitations would be required. Clang had supported full C99 designated init in C++ mode just fine for many years before C++20 appeared.
- calamari4065 3y agoOh man, I've desperately wanted both of these features in basically every language I've used. I think you can do a similar sort of array initialization in C#, but definitely not chained initializers. Those are both so useful, but I can see why they aren't included in C++/++
- loeg 3y ago> all those limitations taken together, and the C++ designated initialization feature is pretty much useless except for the most trivial structs We find it pretty useful even with the limitations. Certainly not “pretty much useless.”
- pjmlp 3y agoContrary to C, in C++ initialization order of data members matters and can have side effects, that is why.
- rwmj 3y agoI think an exception might be made for a plain "C-like" struct that doesn't initialize members or contain anything except basic types. In the specific example[0] the code is actually surrounded by extern "C" { ... } so I suppose that the compiler "knows" this is a plain C struct? (Does extern "C" change parsing rules? I will need to look at what GCC does) [0] https://gitlab.com/nbdkit/nbdkit/-/blob/cd761c9bf770b23f678fd82f0d1c8d4cce2ed1b5/include/nbdkit-plugin.h#L40 https://gitlab.com/nbdkit/nbdkit/-/blob/cd761c9bf770b23f678f...
- loeg 3y agoC++ compilers know when a structure is POD (plain old data) and could relax initialization order rules for them. I doubt they will.
- flohofwoe 3y agoextern "C" { ... } unfortunately only turns off C++ name mangling for symbols, it doesn't switch the C++ compiler into "C language mode". Such a feature would be incredibly useful though for mixing C and C++ code in the same source file.
- deleted 3y ago[deleted]
- gpderetta 3y agoI think, as usual this was the compromise that the committee was able to agree on above all objections. There is stills the possibility that the rules are relaxed if there is agreement. But somebody has to do the work to push it through standardization. I also thought that the behaviour as standardized was useless, but recently I started writing more minimalist code eschewing constructors where aggregate initialisation would suffice, and I haven't really missed the ability to reorder initializers or skip them.
- gumby 3y agoC++ isn’t C and has different structure semantics. Members are initialized in the order defined, which means you can write struct foo { int a = 0; int b = a+1; } If the compiler just did the initialization in the order of declaration, regardless of the order in the initialization list this would not do what you expect: struct obj { int a; int b; } int ival = 0; auto o = obj {.b = ++ival, .a = ival}; o.a would not equal o.b. I would like to have the initialization syntax of C because then one could reorder elements (say for packing reasons) and the designated initialization would “just work”…except it wouldn’t. C++ designated initialization does buy you two things: 1- documentation, but more importantly 2- if you do reorder a struct or class data members the compiler will warn you that your initialization lists are now invalid rather than silently failing. I don’t know how to even find them all in a large code base any other way!
- nemetroid 3y agoInitialization in C++ is already a mess. Making one of the core behaviours (members are initialized in declaration order) work subtly different for this case would make it even more difficult for the programmer to build a correct mental model. From what I can tell, the snippet you posted would compile fine in C++20 mode.