Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I Reference Previous Members of an Initializer List?

Say I want to refer to a member of an initializer_list that I already defined. Can I do it?

This code compiles and gives the expected: "13 55 " in both Visual Studio and gcc, I'd just like to know that it's legal:

const int foo[2] = {13, foo[0] + 42};
like image 982
Jonathan Mee Avatar asked Nov 20 '15 13:11

Jonathan Mee


People also ask

When must you use a member initializer list?

Initializer List is used in initializing the data members of a class. The list of members to be initialized is indicated with constructor as a comma-separated list followed by a colon. Following is an example that uses the initializer list to initialize x and y of Point class.

What is the advantage of initializer list in C++?

The most common benefit of doing this is improved performance. If the expression whatever is the same type as member variable x_, the result of the whatever expression is constructed directly inside x_ — the compiler does not make a separate copy of the object.

What is a member initializer list?

Member initializer list is the place where non-default initialization of these objects can be specified. For bases and non-static data members that cannot be default-initialized, such as members of reference and const-qualified types, member initializers must be specified.

How do initializer lists work?

An initializer list starts after the constructor name and its parameters. The list begins with a colon ( : ) and is followed by the list of variables that are to be initialized – all of​ the variables are separated by a comma with their values in curly brackets.


1 Answers

So what we have here is aggregate initialization covered in section 8.5.1 of the draft C++ standard and it says:

An aggregate is an array or a class [...]

and:

When an aggregate is initialized by an initializer list, as specified in 8.5.4, the elements of the initializer list are taken as initializers for the members of the aggregate, in increasing subscript or member order. Each member is copy-initialized from the corresponding initializer-clause [...]

Although it seems reasonable that side effects from initializing each member of the aggregate should be sequenced before the next, since each element in the initializer list is a full expression. The standard does not actually guarantee this we can see this from defect report 1343 which says:

The current wording does not indicate that initialization of a non-class object is a full-expression, but presumably should do so.

and also notes:

Aggregate initialization could also involve more than one full-expression, so the limitation above to “initialization of a non-class object” is not correct.

and we can see from a related std-discussion topic Richard Smith says:

[intro.execution]p10: "A full-expression is an expression that is not a subexpression of another expression. [...] If a language construct is defined to produce an implicit call of a function, a use of the language construct is considered to be an expression for the purposes of this definition."

Since a braced-init-list is not an expression, and in this case it does not result in a function call, 5 and s.i are separate full-expressions. Then:

[intro.execution]p14: "Every value computation and side effect associated with a full-expression is sequenced before every value computation and side effect associated with the next full-expression to be evaluated."

So the only question is, is the side-effect of initializing s.i "associated with" the evaluation of the full-expression "5"? I think the only reasonable assumption is that it is: if 5 were initializing a member of class type, the constructor call would obviously be part of the full-expression by the definition in [intro.execution]p10, so it is natural to assume that the same is true for scalar types.

However, I don't think the standard actually explicitly says this anywhere.

So this is currently not specified by the standard and can not be relied upon, although I would be surprised if an implementation did not treat it the way you expect.

For a simple case like this something similar to this seems a better alternative:

constexpr int value = 13 ; const int foo[2] = {value, value+42}; 

Changes In C++17

The proposal P0507R0: Core Issue 1343: Sequencing of non-class initialization clarifies the full-expression point brought up here but does not answer the question about whether the side-effect of initialization is included in the evaluation of the full-expression. So it does not change that this is unspecified.

The relevant changes for this question are in [intro.execution]:

A constituent expression is defined as follows:

(9.1) — The constituent expression of an expression is that expression.

(9.2) — The constituent expressions of a braced-init-list or of a (possibly parenthesized) expression-list are the constituent expressions of the elements of the respective list.

(9.3) — The constituent expressions of a brace-or-equal-initializer of the form = initializer-clause are the constituent expressions of the initializer-clause. [ Example:

struct A { int x; }; struct B { int y; struct A a; }; B b = { 5, { 1+1 } }; 

The constituent expressions of the initializer used for the initialization of b are 5 and 1+1. —end example ]

and [intro.execution]p12:

A full-expression is

(12.1) — an unevaluated operand (Clause 8),

(12.2) — a constant-expression (8.20),

(12.3) — an init-declarator (Clause 11) or a mem-initializer (15.6.2), including the constituent expressions of the initializer,

(12.4) — an invocation of a destructor generated at the end of the lifetime of an object other than a temporary object (15.2), or

(12.5) — an expression that is not a subexpression of another expression and that is not otherwise part of a full-expression.

So in this case both 13 and foo[0] + 42 are constituent expression which are part of a full-expression. This is a break from the analysis here which posited that they would each be their own full-expressions.

Changes In C++20

The Designated Initialization proposal: P0329 contains the following addition which seems to make this well defined:

Add a new paragraph to 11.6.1 [dcl.init.aggr]:

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.

We can see this is reflected in the latest draft standard.

like image 155
Shafik Yaghmour Avatar answered Sep 22 '22 14:09

Shafik Yaghmour