On quarta-feira, 26 de julho de 2017 00:50:59 PDT Zhihao Yuan wrote:
> First, the motivation seems not strong enough to
> us. If the struct itself is already trivial, using the
> traditional value-init + assignment approach
>
> stat buf{};
> buf.atime = now;
> ...
It's not equivalent if I'm trying to have a static const variable.
By "initialization order", I was trying to mean "the order the
designated initializers are listed", not "the order the class' member
variables are initialized". Sorry for the misleading term.
What I tried to mean is that we can "repeat the mistake" and initialize
the object using the order its members are declared, regardless the
order the designated initializers are listed, and leaves the compiler to
warn the user if appropriate. We can also allow the user to suppress the
warning using some attribute on his will.
Still, even if it's a mistake for mem-init-clause, for the third party
structures whose definition cannot be controlled by us, the "severity of
the mistake" is mitigated.
struct Agg
{
int x, y;
};
Agg a{5, a.x};
On segunda-feira, 31 de julho de 2017 11:05:09 PDT Nicol Bolas wrote:
> Well, the C++ language doesn't work well with that, since members must be
> initialized in the order they're presented in the class definition. In C++,
> changing the order of members is not a backwards-incompatible change, and
> designated initializers will not change that.
Only because we say it is. There's nothing else preventing initialisation in
any order for trivial types.
Here's the question I have with list and aggregate initialization. Is this well-defined, currently:
struct Agg
{
int x, y;
};
Agg a{5, a.x};
In a braced-init-list, the initializing expressions are evaluated in the order they appear. So it could be that the initialization of `a.x` happens before the expression to start initializing `a.y`. If that is the case, then it's basically impossible to do this out-of-order. Even for trivial types, the initializers could depend on each other in very funky ways.
Personally, I hope that it is not well-defined.
My suggestion would be that designated initializers can be written in any order, but are executed in the order of the struct declaration. As an extra rule the compiler can (must?) check that members are not refered before they are initialized.I really dislike the idea of forcing users to write designated initializers in the same order as the struct declaration have them, not only as the library writer may change the order but more importantly that designated initializers are most useful when you have many members and only initialize a few with non-default values and remembering the order in this case is much harder than remembering the names.Are there other problems with allowing any order of designated initializers besides checking that not yet initialized members are not referred?
In my experience initiing one member from another is very rarely useful, so even if reordering the fields of the struct has a potential to cause compiler errors when compiling users of the struct I don't think this would case very many real problems. If this is the concern I would rather forbid refering to other members from initializers if there are any designators in the initializer.
On Mon, Jul 31, 2017 at 2:42 PM, Thiago Macieira <thi...@macieira.org> wrote:
> It definitely isn't for non-trivial types, but I don't
> know if it should be allowed for trivial ones.
>
> [...]
>
> Either way, that doesn't stop us from allowing:
>
> Agg a { .y = 5, .x = 5 };
>
I've stated my concern with dividing the feature
into trivial types only and those not -- it brings
damage when the library evolves.
However, I feel it's more acceptable if we allocate
a syntax to let people opt-in. I mean, what if
such a discrimination happens only when a
user asks for it by using the = { ... } initializer
(copy-init syntax)?
Well, the C++ language doesn't work well with that, since members must be initialized in the order they're presented in the class definition. In C++, changing the order of members is not a backwards-incompatible change, and designated initializers will not change that.
The <time.h> header shall declare the timespec structure, which shall include at least the following members:
time_t tv_sec Seconds. long tv_nsec Nanoseconds.
The proof that the rule was a mistake is that compilers warn about it. If it wasn't something worth warning people about, they wouldn't put the warning there. So there is clearly an intent that the writer of the constructor ought to put them in the right order.
But if you allow designated initializers to be specified out of member declaration order, it now stops on another "simple and universal rule":
Expression in braced-init-lists are sequenced in the order they appear
So, if you allow unordered designated initializers, which rule wins? Either the members will be initialized out of order, or the expressions will be evaluated out of order.
If sequencing of initializers evaluation order the only issue here, I'm OK with allowing only initializers w/o side effects.
I strongly consider this feature as "named vs positional parameters". It is absolutely useless if we can't use it in order to not depend on declaration order and presence of unexpected members.
Code like
struct C { int a, b, c; };
C c{.c =1, a. = 2};
must "just work"
Don't look at this feature from just the perspective of initializing a bunch of integers for some POSIX interface.
On Tuesday, August 1, 2017 at 7:12:50 PM UTC+3, Nicol Bolas wrote:Don't look at this feature from just the perspective of initializing a bunch of integers for some POSIX interface.
I would say "OS interfaces". They almost always use C.
Also system libraries intended to be used not only from C++ (they even can be written in C++ but expose only C interface like ZeroMQ).
@Nicol: Your argument that "so far brace initializers have always had their expression evaluated in order" is irrelevant as it is just as true to say that "so far initializers have always had their expressions evalutated in member declaration order" as both orders are the same as long as we don't have designators! We have one precedent for what rule to use when the members are named: member initializer lists in constructors. Here the rule is that the expressions run in member declaration order, regardless of which order they are written. Not making these orders the same issues a warning in some compiler due to the same fear that you have: someone will at some point use an uninitialized member to initialize another member having *forgotten* the rules of the language.For the designator case you want to strengthen this rule into a mandatory error if designators are placed in a different order than the member declarations.
Firstly I see this as a bad idea just because it causes the two ways to initiate members to work differently.
As for the "arbitrarily complex expressions" that would prevent the compiler from finding a reference to a member later in the list I don't see this as plausible as it would require sending the whole object to a function in the midst of its initialization or sending the forward refered member itself to a function, which is as easily detected. Maybe if you take the address of the forward refered member and send it to a function that defers it, but that's piling two unlikely construct on top of each other.Further, here are a couple of observations:1) using one member to initiate another is very seldom useful as doing so would more or less indicate that you store the same thing twice. In practice I know that I need this extremely seldom, except for giving the address of one member to the constructor of another for future reference. Much more common is to send "this" itself to some member's constructor, which has its own caveats but which no initiation order limitation can alleviate.
int i = 20;
Agg a{++i, ++i};
2) Making sure that the member init list is in the same order as the member declarations is very tedious and causes lots of warnings especially when adding members. The programmer time spent on doing this tedious work is likely to be many times more than the time lost in finding those rare bugs that thinking that members are initialized in the order that expressions are written would have caused. I think this is safe to say given the extremely low frequency of usage of member a to init member b. Many programmers don't even know that you can do this, as they never needed it.
3) Even with the same initialization order it is easy to make a mistake once you start using one member's value in another members initializer expression either because you didin't think about the ordering or because you re-sorted the members without thinking about it, just reordering the ctor expressions mechanically to silence the warnings.4) The order of members in a constructor's member initializer list is much closer to the member declaration list and thus easier to maintain than initializers at arbitrary distance from the struct declaration.
int i = 20;
Agg a{.mem1 = ++i, .mem2 = ++i};
On Tuesday, August 1, 2017 at 4:50:57 PM UTC-4, Bengt Gustafsson wrote:@Nicol: Your argument that "so far brace initializers have always had their expression evaluated in order" is irrelevant as it is just as true to say that "so far initializers have always had their expressions evalutated in member declaration order" as both orders are the same as long as we don't have designators! We have one precedent for what rule to use when the members are named: member initializer lists in constructors. Here the rule is that the expressions run in member declaration order, regardless of which order they are written. Not making these orders the same issues a warning in some compiler due to the same fear that you have: someone will at some point use an uninitialized member to initialize another member having *forgotten* the rules of the language.For the designator case you want to strengthen this rule into a mandatory error if designators are placed in a different order than the member declarations.
I contest your analogy. As stated by the paper itself, designated initializers is an extension of aggregate initialization, a convenience feature. Changing the order of either the evaluation of the expressions or the order of initialization takes it into the territory of a new feature, not a convenient shortcut.
Firstly I see this as a bad idea just because it causes the two ways to initiate members to work differently.
To the extent that your analogy makes sense, I see your idea as bad because it causes two braced-init-lists to evaluate their expressions in different orders.
It's all a question of whose rules you consider sacrosanct.
As for the "arbitrarily complex expressions" that would prevent the compiler from finding a reference to a member later in the list I don't see this as plausible as it would require sending the whole object to a function in the midst of its initialization or sending the forward refered member itself to a function, which is as easily detected. Maybe if you take the address of the forward refered member and send it to a function that defers it, but that's piling two unlikely construct on top of each other.Further, here are a couple of observations:1) using one member to initiate another is very seldom useful as doing so would more or less indicate that you store the same thing twice. In practice I know that I need this extremely seldom, except for giving the address of one member to the constructor of another for future reference. Much more common is to send "this" itself to some member's constructor, which has its own caveats but which no initiation order limitation can alleviate.
While the use of a member in another member's initializer is one problem, it is not the only problem that evaluation order matters for.
Remember: braced-init-lists are one of the very few places where C++ guarantees that the visible order of expressions represents the actual order of their evaluation. As such, it is very easy to have two expressions be dependent on each other.
Now granted, for designated initializers it's not nearly as easy to accidentally rely on as with regular braced-init-lists (since you can't use `...` pack expansion). But it is still very possible for you to rely on the visible order of expressions, particularly deliberately. After all, guaranteed evaluation order is a feature of braced-init-lists.
Another reason why I contest the member initializer comparison is because of context. Member initializers can only appear in constructors. Indeed, they appear before the actual constructor code. As such, you're fairly limited as to what side-effects you can rely on. Dependency on order of evaluation would thus be through side effects of function calls on parameters, globals, and the state of other members.
By contrast, braced-init-lists can appear pretty much anywhere. As such, there is a lot more local state to foil around with. It's very easy for you to use `++` gymnastics on some local variable and create an ordering dependency.
And remember: braced-init-lists guarantee order of evaluation, so right now:
int i = 20;
Agg a{++i, ++i};
That is completely, 100% well-defined. It does exactly what it says it does, in exactly the order it appears to.
2) Making sure that the member init list is in the same order as the member declarations is very tedious and causes lots of warnings especially when adding members. The programmer time spent on doing this tedious work is likely to be many times more than the time lost in finding those rare bugs that thinking that members are initialized in the order that expressions are written would have caused. I think this is safe to say given the extremely low frequency of usage of member a to init member b. Many programmers don't even know that you can do this, as they never needed it.
Considering that compilers tend to warn about having an out-of-order member initializer list, I contest this observation. If it were so safe and normal, why would there be a warning about it?
3) Even with the same initialization order it is easy to make a mistake once you start using one member's value in another members initializer expression either because you didin't think about the ordering or because you re-sorted the members without thinking about it, just reordering the ctor expressions mechanically to silence the warnings.4) The order of members in a constructor's member initializer list is much closer to the member declaration list and thus easier to maintain than initializers at arbitrary distance from the struct declaration.
That sounds like a really good justification not to do this. The order of expression evaluation, while well-defined, cannot be locally known; it is determined by code "at an arbitrary distance from" the actual object initialization. Why is it a good thing to have the evaluation order of local expression evaluation be controlled due to code contained so far away?
Given the following code:
int i = 20;
Agg a{.mem1 = ++i, .mem2 = ++i};
The way I see it, there are only two reasonable answers for what this code does. Either the values of `mem1` and `mem2` are 21 and 22, or their values are unspecified. That is, either the expressions evaluate in the order they appear, or they evaluate in an order that is unspecified. C++ has lots of places where evaluation order is unspecified, and it has lots of places where evaluation order is locally specified.
But C++ only has one place where the evaluation order is well-and-non-locally-specified. And it's a place that compilers routinely warn about.
To have them evaluate in an order which is both well-defined and defined non-locally is the wrong answer.
Now, if you want to say that the expressions evaluate in the order they appear, but the members are initialized from those expressions in the order they appear in the struct, that might be workable. Though you're still going to have to explain how the possible conversions from the expression type to the member type.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAGsORuDwzS%3DR8pjCoaiBU2GRDKh_%3D8_bdQ6aMGdHxaBcbNLehg%40mail.gmail.com.
On Sat, Aug 5, 2017 at 1:44 PM, Ricardo Fabiano de Andrade
<ricardofabianodeandrade@gmail.com> wrote:
> Based on examples provided by others, I don't see cursed libraries but
> legacy systems (Posix, Win32...), which are industry standards for many
> years and certainly will be around for many more.
>
Cursed, newly written, libraries.
> Supporting -at least- this use case for POD-types by not enforcing order
> would be beneficial both in developer productivity (don't have to deal with
> ordering) and portability (among platforms order is irrelevant).
>
The raised use cases have already being
covered by https://godbolt.org/g/nSk8cr (Matt),
what you are asking for right now is to solve
a solved problem in a specific form rather
than solving a problem itself.
> From a practical point of view, constraining designated initializer to
> declaration order will be a burden and most likely won't be used as much.
>
There is nothing to support your claim.
> Unless, that's the goal - being annoying to use by design. In this case, I
> would rather not have it in the language.
>
It is okay if you don't share our motivations,
but if you don't even bother reading the first
page of the paper,
http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0329r0.pdf
, I have little to help.
--
Zhihao Yuan, ID lichray
The best way to predict the future is to invent it.
_______________________________________________
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAGsORuBVSoUhkMg2_fGYRWzSCwbz%3DSPxUZ6P0c5%2BYmY_KKRLOw%40mail.gmail.com.