What if an initializer of an aggregate modifies members of the aggregate?

60 views
Skip to first unread message

xsk...@gmail.com

unread,
Dec 30, 2017, 11:38:25 PM12/30/17
to ISO C++ Standard - Discussion
Hi all, 

Please consider the following example:
struct S {int a, b;};
S s{s.b = 1, 2};

Does this code well-defined?

The first question is whether the lifetime of s begins in the initializer. In my opinion, according to [basic.life] paragraph 1:

An object is said to have non-vacuous initialization if it is of a class or aggregate type and it or one of its subobjects is initialized by a constructor other than a trivial default constructor.

Here s is not initialized  by a constructor, so the lifetime of s has begun even under initialization. 

Is my understanding above wrong? If it is right, then the initializer s.b = 1 is well-defined, though s.b may have an unspecified value after the initialization of s.a according to [class.ctor] paragraph 14:

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.

Anyway, because the order of initializations is the element order [dcl.init.aggr] paragraph 6:

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.

The initialization of s.b will cause the value 2 to cover the unspecified value, thus s.b will eventually have a value 2.

However, GCC compiles the code and make s.b eventually have a value 1 (LIVE EXAMPLE). 

Is there something wrong with my comprehension or it is just a GCC bug?

In addition, the idea of this problem comes from that question.

Thanks,
xskxzr

Chris Hallock

unread,
Dec 31, 2017, 10:22:21 PM12/31/17
to ISO C++ Standard - Discussion, xsk...@gmail.com


On Saturday, December 30, 2017 at 8:38:25 PM UTC-8, xsk...@gmail.com wrote:
Hi all, 

Please consider the following example:
struct S {int a, b;};
S s{s.b = 1, 2};

Does this code well-defined?

I think it is valid and yields either {(unspecified value), 2} or {1, 2}, depending on whether [class.ctor]/14 applies to all initialization or just constructor calls (it's not clear to me).

 
The first question is whether the lifetime of s begins in the initializer. In my opinion, according to [basic.life] paragraph 1:

An object is said to have non-vacuous initialization if it is of a class or aggregate type and it or one of its subobjects is initialized by a constructor other than a trivial default constructor.

Here s is not initialized  by a constructor, so the lifetime of s has begun even under initialization.

Agreed.

 
Is my understanding above wrong? If it is right, then the initializer s.b = 1 is well-defined, though s.b may have an unspecified value after the initialization of s.a according to [class.ctor] paragraph 14:

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.

That clause, if it's intended to apply to any initialization and not just constructor calls, makes s.a initialized to an unspecified value, since the result of s.b = 1 is an lvalue referring to s.b.

 
Anyway, because the order of initializations is the element order [dcl.init.aggr] paragraph 6:

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.

The initialization of s.b will cause the value 2 to cover the unspecified value, thus s.b will eventually have a value 2.

However, GCC compiles the code and make s.b eventually have a value 1 (LIVE EXAMPLE).

Agreed. GCC is wrong.


Richard Smith

unread,
Jan 2, 2018, 4:07:20 PM1/2/18
to std-dis...@isocpp.org, xsk...@gmail.com
Earlier revisions of the standard could be read as making the determination of whether constant or dynamic initializations is performed on a per-object basis, not on a per-variable basis. Some compilers (GCC included) use that model to perform a potentially-important optimization: when aggregate-initializing a global, emit any constant portions of the initializer as part of the initial value of the object, and only dynamically execute the portions of the initializer that cannot be reduced to a constant. (Consider the case of initializing a very large table, where only a very small number of the subobjects have dynamic initialization and the rest is constant data. It's desirable to emit the constant data as data rather than as initialization code, and to use initialization code only for the actually-dynamic portion.)

This seems like something the committee should explicitly discuss; the optimization seems valuable, even though it violates the normal semantic rules for initialization.
Reply all
Reply to author
Forward
0 new messages