6 ms·
> Why can't you just do this at the language level like any sane person? The reality is C++ is a ridiculously complex and legacy-ridden language, with a diffic
by ratmeadow 2y ago
> Why can't you just do this at the language level like any sane person?
The reality is C++ is a ridiculously complex and legacy-ridden language, with a difficult goal to preserve backwards compatibility. I haven't read the history on the keyword args proposals but I'm guessing they were declined due to a deluge of silly edge case interactions with C++ semantics that became too hard to work around. Like how struct designated initialisers have to be in order of member declaration due to object lifetime rules or something like that.
I would recommend trying to not be outraged at the state of C++ these days. It's time to stop hoping that C++ gets the nice features we need in any sort of reasonable manner. The reality of the language is not compatible with much niceness.
- drysine 2y ago>Like how struct designated initialisers have to be in order of member declaration due to object lifetime rules or something like that. It matters in which order sub-objects are initialized - if you have a class with the members A and B, and B takes pointer or reference to A in its constructor and does something with A, A better be already initialized. Sub-objects are initialized in the order of their declaration and having different order of designed initializers would be confusing. In fact, exactly that problem we have in C++ with the list of initializers of base classes and data members in constructors.
- badmintonbaseba 2y agoYou can't easily take the pointer to an other member in a designated initializer. It's still a problem for members that are implicitly initialized by their default member initializer, but that can be sorted out.
- deleted 2y ago[deleted]
- drysine 2y agoI think it is easy enough to be a potential footgun [0]: struct A { A(A*);}; struct B { A a1; A a2; }; void f() { B b{.a1 = A(nullptr), .a2 = A(&b.a1) }; } [0] https://godbolt.org/z/cGaxzh17T https://godbolt.org/z/cGaxzh17T
- badmintonbaseba 2y agoThat's a stretch to call it "easy enough", you are explicitly pointing the gun at your foot. That `b.a1` might not be explicitly UB, but that's quite suspect when b's lifetime didn't start yet. Accessing members through `this` in constructors have some special allowance to not make that UB.
- drysine 2y agoGood point, I should've used direct initialization in this example. struct A { int x; A(A *a) { if (a) a->x = 42;} }; struct B { A a1; A a2; }; void f() { B b{.a1{nullptr}, .a2{&b.a1} }; } This code is valid. Now if I change the struct definition to struct B { A a2; A a1; }; it will become UB. Luckily it won't compile because of the difference between the order of declaration and the order of designated initializers. The alternative way is to always initialize the sub-objects in the order of designated initializers (what do we do if not all initializers are provided?), but this would mean that the order of constructor calls wouldn't match the (reversed) order of destructor calls. Or we would need to select the destructor dynamically based on the way the object was initialized. https://godbolt.org/z/MPoqEhTvf https://godbolt.org/z/MPoqEhTvf
- badmintonbaseba 2y agoMy gripe was not the form of initialization of the elements, but forming `b.a1` before `b`'s lifetime has started. It hasn't started before all of the elements are initialized.
- drysine 2y ago
- pilianas 2y agoYou still don't need to syntactically require same order initialization, it's an easy job for a compiler to reorder things so that all dependencies work out - every language with order independent declarations have to do that for example.
- drysine 2y agoIn a general case you can't do that with separate compilation. [0] struct A { A(A*); }; A* f(struct B *b); struct B { A a1; A a2; B(): a1(f(this)), a2(f(this)) {} }; //in a different translation unit A* f(B *b) { return &b->a1; //or a2, we don't know } [0] https://godbolt.org/z/xMb64ssYK https://godbolt.org/z/xMb64ssYK
- kccqzy 2y agoYour code snippet does not use the designated initializer feature that this comment thread is talking about. Furthermore your code possibly contains undefined behavior depending on the behavior of the constructor of A.
- drysine 2y agoAs I stated above, it's the same kind of situation. See my another comment in this thread for an example with designated initializers. >your code possibly contains undefined behavior Only if f() returns a pointer to a2 (which is my point). Or did you imply that in the case when f() returns a pointer to a1 and it gets passed to the constructor of a1, provenance matters?
- drysine 2y agoActually, provenance matters: "During the construction of an object, if the value of the object or any of its subobjects is accessed through a glvalue that is not obtained, directly or indirectly, from the constructor's this pointer, the value of the object or subobject thus obtained is unspecified." [0] Reading an unspecified value isn't UB, that's good, but I don't understand why the standard says 'unspecified' because it clearly can be indeterminate if a sub-object hasn't been initialized yet. [0] https://eel.is/c++draft/class.cdtor#2 https://eel.is/c++draft/class.cdtor#2