Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

P0329R0 falls short for designated initialisers

381 views
Skip to first unread message

Thiago Macieira

unread,
Jul 26, 2017, 12:25:03 AM7/26/17
to std-pr...@isocpp.org
There are two extra requirements for the C++ version of the feature that don't
exist in C99:
1) initialisers must appear in the order of declaration
2) nesting not permitted.

That means you cannot write:

time_t now = time(nullptr);
struct stat stbuf = {
.st_dev = 0,
.st_ino = 0,
.st_mode = S_IFREG | 0644,
.st_nlink = 1,
.st_uid = 0,
.st_gid = 0,
.st_rdev = 0,
.st_size = 123,
.st_atime = now,
.st_mtime = now,
.st_ctime = now,
.st_blocksize = 512,
.st_blocks = 1
};

Even though I followed the POSIX documentation
http://pubs.opengroup.org/onlinepubs/009695399/basedefs/sys/stat.h.html

The above has both problems:
1) the order of the fields is not the same in all OSes, not even in the
multiple architectures of a given OS. On Linux, for example, the order of
st_mode and st_nlink is reversed on x86 32-bit and both st_blocksize and
st_blocks occur before the time fields

2) the three time fields are actually macros:
# define st_atime st_atim.tv_sec /* Backward compatibility. */
# define st_mtime st_mtim.tv_sec
# define st_ctime st_ctim.tv_sec

The code above works fine for C99 but not C++.

That means designated initialisers cannot be used on structures you don't
control, since the order may vary from implementation to implementation. And
this trick of #defining the name of a member to point to a sub-field is actually
very common, sometimes taken to an extreme, like in siginfo_t.

So, to keep compatibility with C99, can we relax both rules for (at least)
trivial types?

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center

d25f...@outlook.com

unread,
Jul 26, 2017, 3:39:59 AM7/26/17
to std-pr...@isocpp.org, Thiago Macieira
Given that `.d { .d2 = 2 },` is allowed in the proposal (p5), I don't
see the reason why `.e.a = 2,` is forbidden (p2).

But except for the case here, nesting designators seems to make code
hard to read. (But this is a coding convention matter.)

>
> That means designated initialisers cannot be used on structures you don't
> control, since the order may vary from implementation to implementation. And
> this trick of #defining the name of a member to point to a sub-field is actually
> very common, sometimes taken to an extreme, like in siginfo_t.
>
> So, to keep compatibility with C99, can we relax both rules for (at least)
> trivial types?
>
Not for the compatibility with C99, but I still agree that the rule #1
should be relaxed.

For trivial types it should be straightforward.

I also think we may further allow the initialization order to be
inconsistent with declaration order of the data members (while keep the
evaluation order from left to right), as it's already allowed today in
constructor' member initializer list (*), and thus relax rule #1 for
non-trivial types.

We might also introduce new attribute `[[unordered_init]]` or something
alike to suppress warnings the compiler issues when initialization order
is not consistent with the declaration order.

---

*: But still there is a difference that when writing constructor's
member initializer list, the definition of the class in question is
controlled by us, while when using designator initializers the structure
to be initialized may be defined by third parties.

Zhihao Yuan

unread,
Jul 26, 2017, 3:51:03 AM7/26/17
to std-pr...@isocpp.org
On Tue, Jul 25, 2017 at 11:24 PM, Thiago Macieira <thi...@macieira.org> wrote:
> So, to keep compatibility with C99, can we relax both rules for (at least)
> trivial types?
>

The authors considered this option, but do not
feel this is a viable choice.

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;
...

can expect a codegen that is identical to using
designated initializers. At least that's what gcc
does; if you found that your compiler produces
different code and one of which is suboptimal,
you should file a bug to the vendor.

Second, relaxing these rules to trivial types can
bring some negative consequences. Consider
that you shipped a library with an option struct,
initially a trivial type, an innocent user in his/her
code initialized an object of such a type using
unordered designators, in next version of the
library you appended a `std::string` field to that
struct, then the user's code breaks. Designated
initializers intend to establish a way to reduce
such breakage caused by library evolving, but
relaxing the rules to trivial types can defeat such
a purpose.

--
Zhihao Yuan, ID lichray
The best way to predict the future is to invent it.
_______________________________________________

Zhihao Yuan

unread,
Jul 26, 2017, 4:12:26 AM7/26/17
to std-pr...@isocpp.org, Thiago Macieira
On Wed, Jul 26, 2017 at 2:39 AM, d25f...@outlook.com
<d25f...@outlook.com> wrote:
> For trivial types it should be straightforward.
>

No, explained in the previous email. It
makes the language harder to use.

> I also think we may further allow the initialization order to be
> inconsistent with declaration order of the data members (while keep the
> evaluation order from left to right), as it's already allowed today in
> constructor' member initializer list (*), and thus relax rule #1 for
> non-trivial types.

First, mem-init-clauses are evaluated in the
member declaration orders rather than the
written order. It's a design mistake and let's
not repeat that. The approach you implied,
thus, allowing the evaluation order diverges
from the initialization order, will make this
feature not pay-for-what-you-use. These
are already explained in the paper.

d25f...@outlook.com

unread,
Jul 26, 2017, 6:18:56 AM7/26/17
to std-pr...@isocpp.org, Zhihao Yuan, Thiago Macieira
On 2017/7/26 16:12, Zhihao Yuan wrote:
> On Wed, Jul 26, 2017 at 2:39 AM, d25f...@outlook.com
> <d25f...@outlook.com> wrote:
>> For trivial types it should be straightforward.
>>
>
> No, explained in the previous email. It
> makes the language harder to use.
>
>> I also think we may further allow the initialization order to be
>> inconsistent with declaration order of the data members (while keep the
>> evaluation order from left to right), as it's already allowed today in
>> constructor' member initializer list (*), and thus relax rule #1 for
>> non-trivial types.
>
> First, mem-init-clauses are evaluated in the
> member declaration orders rather than the
> written order. It's a design mistake and let's
> not repeat that. The approach you implied,
> thus, allowing the evaluation order diverges
> from the initialization order, will make this
> feature not pay-for-what-you-use. These
> are already explained in the paper.
>

I think I expressed my thoughts wrong.

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. Personally I do not take it as a "mistake" as
there's otherwise no easy way to initialize those "weird" third party
structures (*) other than falls back to what we have today.

Also, in this way, there should be no temporary involved.
("pay-for-what-you-use")

---

*: Those whose definitions are changing from OS to OS, from ISA to ISA
(as in Thiago's post), or not declared in a logical order (presumbly due
to backward compatibility -- New fields can only be added at the end of
the structure).

Thiago Macieira

unread,
Jul 26, 2017, 11:53:41 AM7/26/17
to std-pr...@isocpp.org
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. Taking an
example from Linux kernel's source code:

static const struct xattr_handler shmem_security_xattr_handler = {
.prefix = XATTR_SECURITY_PREFIX,
.get = shmem_xattr_handler_get,
.set = shmem_xattr_handler_set,
};

A lot of this is done for virtual tables, but we are faced with this in C++
code as well when interfacing with C libraries. Examples from Qt source code:

http://code.qt.io/cgit/qt/qtwayland.git/tree/src/client/
qwaylanddisplay.cpp#n349
http://code.qt.io/cgit/qt/qtwayland.git/tree/src/hardwareintegration/client/
brcm-egl/qwaylandbrcmeglwindow.cpp#n83
http://code.qt.io/cgit/qt/qtwayland.git/tree/tests/auto/client/client/
mockcompositor.cpp#n321
http://code.qt.io/cgit/qt/qtwayland.git/tree/tests/auto/client/client/
mockshell.cpp#n149
http://code.qt.io/cgit/qt/qtwayland.git/tree/tests/auto/client/client/
mockshell.cpp#n171

We are forced to use non-designated initialisation, which means we could end
up with:

static const struct operations ops = {
nullptr,
nullptr,
nullptr,
nullptr,
do_open,
nullptr,
nullptr,
noop,
nullptr,
do_close
};

Matt Calabrese

unread,
Jul 26, 2017, 12:19:56 PM7/26/17
to ISO C++ Standard - Future Proposals
On Wed, Jul 26, 2017 at 11:53 AM, Thiago Macieira <thi...@macieira.org> wrote:
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.

IIUC, you can still get constant initialization and also avoid dependence on member order even in that case. It doesn't require designated initializers:


-- Matt Calabrese

Zhihao Yuan

unread,
Jul 26, 2017, 1:10:26 PM7/26/17
to d25f...@outlook.com, Thiago Macieira, std-pr...@isocpp.org
On Jul 26, 2017 5:18 AM, "d25f...@outlook.com" <d25f...@outlook.com> wrote:

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.

The severity of the mistake is increased.
You essentially get a feature to
encourage silently breaking users'
legitimate code which relies on a
specific evaluation order of the
initializers by changing the member
declaration order at the library side.

--
Zhihao

Nicol Bolas

unread,
Jul 26, 2017, 1:49:20 PM7/26/17
to ISO C++ Standard - Future Proposals, d25f...@outlook.com, thi...@macieira.org, z...@miator.net

I think another way to look at it is this. Designated initializers are just a convenience form of aggregate initialization (one that notably allows you to skip members and let them be initialized via their default member initializers). If you couldn't guarantee the order of the items in the struct, then you couldn't use aggregate initialization to begin with.  And thus, you shouldn't be surprised that you can't use designated initializers.

Initializing members out-of-order through aggregate initialization would be a new thing, not a convenience feature.

Nicol Bolas

unread,
Jul 26, 2017, 1:50:36 PM7/26/17
to ISO C++ Standard - Future Proposals, d25f...@outlook.com, thi...@macieira.org, z...@miator.net

But at least with designated initializers, if you initialize them out-of-order, then at least you get a compile error, so that you can fix your code. With regular aggregate initialization, you wouldn't even get that.

Zhihao Yuan

unread,
Jul 26, 2017, 5:33:16 PM7/26/17
to Nicol Bolas, ISO C++ Standard - Future Proposals, d25f...@outlook.com, Thiago Macieira, Zhihao Yuan
On Wed, Jul 26, 2017 at 12:49 PM, Nicol Bolas <jmck...@gmail.com> wrote:
> Designated initializers are just
> a convenience form of aggregate initialization (one that notably allows you
> to skip members and let them be initialized via their default member
> initializers).

That's exactly how we model this feature
when we were designing it; stated as the 2nd
motivation in the paper.

> Initializing members out-of-order through aggregate initialization would be
> a new thing, not a convenience feature.

Well, we tried very hard on that, though it's not
a part of the motivation, but nothing really works
so far.

Victor Dyachenko

unread,
Jul 31, 2017, 7:28:09 AM7/31/17
to ISO C++ Standard - Future Proposals
I support Thiago. This valid C99/11 code should also be valid С++:

#include<time.h>

struct timespec1 { time_t tv_sec; long tv_nsec; };
struct timespec2 { long tv_nsec; time_t tv_sec; };
struct timespec3 { time_t tv_sec; long tv_nsec; int extra_field; };

struct timespec1 t1 = { .tv_sec = 1, .tv_nsec = 0 };
struct timespec2 t2 = { .tv_sec = 1, .tv_nsec = 0 };
struct timespec3 t3 = { .tv_sec = 1, .tv_nsec = 0 };

Thiago Macieira

unread,
Jul 31, 2017, 12:41:13 PM7/31/17
to std-pr...@isocpp.org
Something I found over the weekend:

https://gcc.gnu.org/onlinedocs/gcc/Common-Type-Attributes.html#Common-Type-Attributes

designated_init

This attribute may only be applied to structure types. It indicates that
any initialization of an object of this type must use designated initializers
rather than positional initializers. The intent of this attribute is to allow
the programmer to indicate that a structure’s layout may change, and that
therefore relying on positional initialization will result in future breakage.

GCC emits warnings based on this attribute by default; use -Wno-
designated-init to suppress them.

So GCC devs have already foreseen the need for types that may change order in
the future.

Nicol Bolas

unread,
Jul 31, 2017, 2:05:10 PM7/31/17
to ISO C++ Standard - Future Proposals

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.

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.


Thiago Macieira

unread,
Jul 31, 2017, 2:24:46 PM7/31/17
to std-pr...@isocpp.org
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.

Nicol Bolas

unread,
Jul 31, 2017, 2:34:04 PM7/31/17
to ISO C++ Standard - Future Proposals
On Monday, July 31, 2017 at 2:24:46 PM UTC-4, Thiago Macieira wrote:
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.

Well, there's the question I just posted. Do you know if that code is legal at present? If it is legal, then that would most assuredly prevent arbitrary initialization ordering for members, even for trivial types.

Thiago Macieira

unread,
Jul 31, 2017, 3:42:13 PM7/31/17
to std-pr...@isocpp.org
On segunda-feira, 31 de julho de 2017 11:34:04 PDT Nicol Bolas wrote:
> Well, there's the question I just posted. Do you know if that code is legal
> at present? If it is legal, then that would most assuredly prevent
> arbitrary initialization ordering for members, even for trivial types.

I don't know if it is. It definitely isn't for non-trivial types, but I don't
know if it should be allowed for trivial ones. The question to be answered is
whether the evaluation of a.x happens before the assignment of 5 to it in the
brace initialiser.

If we say that in a trivial type's brace initialisation each type is
initialised left-to-right and evaluation happens independently, then it would
be valid.

I don't think we want to say that. That would be supported by the fact that
P0329 also disallows reusing a member in the expression for initialisation of
another type (which C99 allows and I'm not asking for).

Either way, that doesn't stop us from allowing:

Agg a { .y = 5, .x = 5 };

Barry Revzin

unread,
Jul 31, 2017, 4:36:52 PM7/31/17
to ISO C++ Standard - Future Proposals
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.


I think that's well-formed. Each element's initialization is sequenced before the next element's initialization (http://eel.is/c++draft/dcl.init.aggr#6), so we'd copy-initialize a.x from 5 and then copy-initialize a.y from a.x. 

Nicol Bolas

unread,
Jul 31, 2017, 5:08:02 PM7/31/17
to ISO C++ Standard - Future Proposals

That must be a very recent change, because N4659 doesn't say that. I wonder where that line came from. For the sake of clarity, [dcl.init.aggr]/6 says:

> The initializations of the elements of the aggregate are evaluated in the element order. That is, all value computations and side effects associated with a given element are sequenced before those of any element that follows it in order.


Daniel Krügler

unread,
Jul 31, 2017, 5:23:47 PM7/31/17
to std-pr...@isocpp.org
2017-07-31 23:08 GMT+02:00 Nicol Bolas <jmck...@gmail.com>:
> On Monday, July 31, 2017 at 4:36:52 PM UTC-4, Barry Revzin wrote:
>> I think that's well-formed. Each element's initialization is sequenced
>> before the next element's initialization
>> (http://eel.is/c++draft/dcl.init.aggr#6), so we'd copy-initialize a.x from 5
>> and then copy-initialize a.y from a.x.
>
> That must be a very recent change, because N4659 doesn't say that. I wonder
> where that line came from.

It was part starting from the first p0329 wording,

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0329r2.pdf

and was voted in by acceptance of p0329r4 during the Toronto meeting
(Soon visible in the publication list).

- Daniel

Nicol Bolas

unread,
Jul 31, 2017, 5:30:17 PM7/31/17
to ISO C++ Standard - Future Proposals

Oh, so that's not actually part of C++17. Well, that changes things.

The point of the question was to determine if it was legal for people to write code where the initialization expressions could rely on initialization order. If that wasn't the case, then we could have unordered designated initializers (for trivial types only) by specifying that the expressions are resolved in the order of the braced-init-list, but they initialize the objects in the order they appear in the aggregate. This would allow the "as if" rule to come into play, allowing implementations to do what they like, while still allowing us to know that subobjects are initialized in order.

The goal was to avoid doing what Thaigo's suggestion would require: completely redefining member subobject initialization order rules.

So long as we make it undefined again before C++20 comes out, then we can take the quick route to doing so.

Bengt Gustafsson

unread,
Jul 31, 2017, 7:07:34 PM7/31/17
to ISO C++ Standard - Future Proposals

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.

Thiago Macieira

unread,
Jul 31, 2017, 8:23:11 PM7/31/17
to std-pr...@isocpp.org
On segunda-feira, 31 de julho de 2017 16:07:34 PDT Bengt Gustafsson wrote:
> 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.

For non-trivial, yes, there are issues. Take this example:

struct Agg
{
SubObj1 a;
SubObj2 b;
};

Agg a = { .b = 1, .a = 2 };

What happens if b's constructor throws?

That's why I am asking for the any order permission to apply only to trivial
types. Which all structs defined in headers meant to be consumed by C are.

Nicol Bolas

unread,
Jul 31, 2017, 8:49:44 PM7/31/17
to ISO C++ Standard - Future Proposals
On Monday, July 31, 2017 at 7:07:34 PM UTC-4, Bengt Gustafsson wrote:

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?

Yes.

Braced-init-lists have a very simple rule at present: the expressions are executed in the order they appear. This will be true, whether they call a constructor or initialize an aggregate.

What this will do is turn that rule into a much more complex one. To know the order that those expressions will be executed in, you must look at non-local code. And therefore, it becomes very possible to make two expressions in a braced-init-list depend on the order of execution, but to have that order no longer match the visible order of the expressions. Or worse, that your code works fine at one point, but then a change to the order of the members causes a silent breakage. With enforced ordering, it's a noisy breakage.

This is the fundamental reason why allowing member initializers to work this way was a bad idea. This will effectively lead to compilers having to issue warnings about designated initializers being executed out of their apparent order.
 
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.

You cannot effectively forbid that sort of thing. You can always have one initializer reference itself by getting a pointer and storing it locally, then have another initializer expression read from that pointer. Or have two functions which have some shared state, such that their order of execution matters. And so forth.

Zhihao Yuan

unread,
Jul 31, 2017, 10:28:55 PM7/31/17
to std-pr...@isocpp.org
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)? Examples:

Agg a = { .y = 5, .x = 5 }; // ok
Agg b { .y = 5, .x = 5 }; // no
struct X { Agg a; };
X c { { .a{ .y = 5, .x = 5 } }; // no
X c { { .a = { .y = 5, .x = 5 } }; // ok

Nicol Bolas

unread,
Jul 31, 2017, 11:13:09 PM7/31/17
to ISO C++ Standard - Future Proposals, z...@miator.net
On Monday, July 31, 2017 at 10:28:55 PM UTC-4, Zhihao Yuan wrote:
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)?

Right, because the thing we most need to do is make "uniform initialization" less uniform.

No, having copy-list-initialization work (almost) exactly like direct-list-initialization is one of the best aspects of list-initialization. We should not undo that lightly.

If unordered designated initializers are dangerous enough to need their own special syntax, then they're too dangerous for C++.

Thiago Macieira

unread,
Aug 1, 2017, 1:16:01 AM8/1/17
to std-pr...@isocpp.org
On segunda-feira, 31 de julho de 2017 19:28:51 PDT Zhihao Yuan wrote:
> > 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.

Understood, but it cannot happen for C structures. C developers are the ones
who write structures without constructors and are used to designated
initialisers. It's their structures we have to worry about changing layout.

C++ developers usually add constructors.

> 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)? Examples:
>
> Agg a = { .y = 5, .x = 5 }; // ok
> Agg b { .y = 5, .x = 5 }; // no
> struct X { Agg a; };
> X c { { .a{ .y = 5, .x = 5 } }; // no
> X c { { .a = { .y = 5, .x = 5 } }; // ok

How about:

X = { .a.x = 5, .a.y = 5 }; // ok or not?

Zhihao Yuan

unread,
Aug 1, 2017, 1:36:11 AM8/1/17
to std-pr...@isocpp.org
On Tue, Aug 1, 2017 at 12:15 AM, Thiago Macieira <thi...@macieira.org> wrote:
>
> C++ developers usually add constructors.
>

That will be too ideal. There is a difference
between you break intentionally, and you
break without knowing...

>> struct X { Agg a; };
>> X c { { .a{ .y = 5, .x = 5 } }; // no
>> X c { { .a = { .y = 5, .x = 5 } }; // ok
>
> How about:
>
> X = { .a.x = 5, .a.y = 5 }; // ok or not?
>

I think yes. But my idea seems lacking
support. One of the co-authors does
not consider this subtle difference to be
teachable. The last time we discriminated
= {} from {} to support "C-only" rules was
in C++11, to limit brace elision, but later
we just enabled it to {} as well :(

Zhihao Yuan

unread,
Aug 1, 2017, 1:41:38 AM8/1/17
to std-pr...@isocpp.org
On Wed, Jul 26, 2017 at 11:19 AM, 'Matt Calabrese' via ISO C++
Standard - Future Proposals <std-pr...@isocpp.org> wrote:
>
> IIUC, you can still get constant initialization and also avoid dependence on
> member order even in that case. It doesn't require designated initializers:
>
> https://godbolt.org/g/nSk8cr
>

This is such a kind of smart solution, which
I personally would be totally fine to practice,
but if being a leader on a team, I can't really
decide to make it into the coding style lol.

Thiago Macieira

unread,
Aug 1, 2017, 1:58:31 AM8/1/17
to std-pr...@isocpp.org
On segunda-feira, 31 de julho de 2017 22:36:07 PDT Zhihao Yuan wrote:
> > C++ developers usually add constructors.
>
> That will be too ideal. There is a difference
> between you break intentionally, and you
> break without knowing...

By adding or replacing a meber with a non-trivial one or by adding an explicit
constructor, a lot of things change. For example, until we get destructive
moves, std::vector will suddenly become less efficient if you make your type
non-trivial. Developers have to, unfortunately, be quite aware of the issue.

Again, my worry is C developers who aren't used to C++ limitations.

Victor Dyachenko

unread,
Aug 1, 2017, 3:23:48 AM8/1/17
to ISO C++ Standard - Future Proposals
On Monday, July 31, 2017 at 9:05:10 PM UTC+3, 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.

POSIX for example never specifies the order of members. Also it doesn't specify full set of members, just required ones:

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. 

So when we write cross-platform code we want sometimes use structure initialization but don't want depend on members order and exact number of them:

struct TimeSpec : public ::timespec
{
    constexpr TimeSpec(time_t sec, long nsec)
        : ::timespec{.tv_sec = sec, .tv_nsec = nsec} {}
};

The order of initialization must not change! There is the only initialization order in C++: order of members declaration. The compiler can emit warning as with constructor init-list. Can't we just adopt the same rule as for constructor init-list? (And I don't think that this rule was a mistake as it was stated above)

Victor Dyachenko

unread,
Aug 1, 2017, 3:32:14 AM8/1/17
to ISO C++ Standard - Future Proposals
More examples

const ::iovec v[] =
{
    ::iovec{.iov_base = buf1, .iov_len = buf1_len },
    ::iovec{.iov_base = buf2, .iov_len = buf2_len }
};
::writev(fd, v, array_size(v));

constexpr sockaddr_in my_endpoint =
{
    .sin_family = AF_INET,
    .sin_port = htons(123),
    .sin_addr = INADDR_ANY
};

Magnus Fromreide

unread,
Aug 1, 2017, 4:26:50 AM8/1/17
to std-pr...@isocpp.org
System headers contains lots of insanity.
This is copied from Linux/glibc.

struct sigaction
{
/* Signal handler. */
#ifdef __USE_POSIX199309
union
{
/* Used if SA_SIGINFO is not set. */
__sighandler_t sa_handler;
/* Used if SA_SIGINFO is set. */
void (*sa_sigaction) (int, siginfo_t *, void *);
}
__sigaction_handler;
# define sa_handler __sigaction_handler.sa_handler
# define sa_sigaction __sigaction_handler.sa_sigaction
#else
__sighandler_t sa_handler;
#endif
/* more members follows */
};

This also talks against disallowing member.submember = value initialization
since it is quite reasonable to assume that

sigaction sa = { .sa_handler = &function };

should work.

/MF

Nicol Bolas

unread,
Aug 1, 2017, 10:36:03 AM8/1/17
to ISO C++ Standard - Future Proposals

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.

And the same goes here.People generally don't like to leave spurious warnings in their code if they can help it (this is 90% of the impetus for allowing `container::size_type` to be signed). So the only code where this warning will crop up is code where the user needs to declare them out of order (as in the POSIX case).

If it's something that shouldn't be done (hence the warning), then we should forbid it outright. And if it's something that should be done, then we should do it in such a way that it does exactly what it looks like it does.

Victor Dyachenko

unread,
Aug 1, 2017, 11:12:00 AM8/1/17
to ISO C++ Standard - Future Proposals
On Tuesday, August 1, 2017 at 5:36:03 PM UTC+3, Nicol Bolas wrote:
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.

Again. The intention was to keep the simple and  universal rule:

Initialization order always defined by members declaration order

We don't want to change this rule with introducing one more initialization style.

Nicol Bolas

unread,
Aug 1, 2017, 11:47:08 AM8/1/17
to ISO C++ Standard - Future Proposals

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.

Victor Dyachenko

unread,
Aug 1, 2017, 11:50:51 AM8/1/17
to ISO C++ Standard - Future Proposals
On Tuesday, August 1, 2017 at 6:47:08 PM UTC+3, Nicol Bolas wrote:
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.

Which rule wins now  in case of constructors init-list? Apply the same rule here.

Nicol Bolas

unread,
Aug 1, 2017, 11:55:47 AM8/1/17