Can a (C/C++) array initialization reference itself? [duplicate] - c++

This question already has an answer here:
Can I Reference Previous Members of an Initializer List?
(1 answer)
Closed 11 months ago.
I was wondering about an initialization of the following form:
int array[] = {
v - 1,
array[0] + 1
} ;
In the initialization of the second element, the value of the first is used, but the entire array is not yet initialized. This happens to compile with g++, but I was unsure whether this is actually portable and a well defined construct?

See 3.3.2 Point of declaration:
The point of declaration for a name is immediately after its complete declarator (Clause 8) and before its
initializer (if any), except as noted below. [ Example:
int x = 12;
{ int x = x; }
Here the second x is initialized with its own (indeterminate) value. —end example ]
So you are referring to the array correctly, its name is known after the =.
Then, 8.5.1 Aggregates:
An aggregate is an array or a class [...]
17: The full-expressions in an initializer-clause are evaluated in the order in which they appear.
However, I see no reference to when the evaluated values are actually written into the array, so I wouldn't rely on this and would even go so far to declare your code as not well defined.

As far as I can see, this is not well defined. The standard (C++11, 8.5.1/17) specifies that "The full-expressions in an initializer-clause are evaluated in the order in which they appear", but I can't see anything that requires each aggregate element to be initialised from the result of its initializer-clause before the next is evaluated.

Can a (C/C++) array initialization reference itself?
This is also valid C code.
C has some correspondent paragraph (emphasis mine).
(C99, 6.2.1p7) "Structure, union, and enumeration tags have scope that begins just after the appearance of the tag in a type specifier that declares the tag. Each enumeration constant has scope that begins just after the appearance of its defining enumerator in an enumerator list. Any other identifier has scope that begins just after the completion of its declarator."

I think this is handled by http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1343 . Initially my report was only about non-class initializers for namespace scope objects (see When exactly is an initializer temporary destroyed?), but the problem exists for aggregate elements just aswell if they are non-class. And as the additional recent note explains, even seems to exist for the entire aggregate initialization aswell, even if it is a class object, because then no constructor call happens that would enlargen the full-expression of the initializer.
If instead of int you would have used a class, and the initialization would be a constructor call, then that constructor call would be part of the same full expression that encloses the aggregate-ininitializer element, so that here the order would be OK and your code would be well-defined.

Related

Contradictory definitions about the Order of Constant Initialization and Zero Initialization in C++

I have been trying to understand how static variables are initialized. And noted a contradiction about the order of constant initialization and zero initialization at cppref and enseignement.
At cppref it says:
Constant initialization is performed instead of zero initialization of the static and thread-local (since C++11) objects and before all other initialization.
Whereas at enseignement it says:
Constant initialization is performed after zero initialization of the static and thread-local objects and before all other initialization.
So as you can see cppref uses "instead" while the second site uses "after". Which of the two is correct? That is, does zero initialization always happen first and then if possible constant initialization as implied by the second site or the other way round.
The example given there is as follows:
#include <iostream>
#include <array>
struct S {
static const int c;
};
const int d = 10 * S::c; // not a constant expression: S::c has no preceding
// initializer, this initialization happens after const
const int S::c = 5; // constant initialization, guaranteed to happen first
int main()
{
std::cout << "d = " << d << '\n';
std::array<int, S::c> a1; // OK: S::c is a constant expression
// std::array<int, d> a2; // error: d is not a constant expression
}
This is my understanding of the initialization process so far:
Static initialization happens first. This includes
Constant initialization if possible
Zero initialization only if constant initialization was not done
Dynamic Initialization
Now according to the above(my understanding) this is how the code above works:
Step 1.
When the control flow reaches the definition of const int d it sees that the initializer has a variable(namely S::c) that has not been already initialized. So the statement const int d = 10 * S::c; is a dynamic time(runtime) initialization. This means it can only happen after static initialization. And ofcourse d is not a constant expression.
Step 2.
The control flow reaches the definition of variable const int S::c; . In this case however the initializer is a constant expression and so constant initialization can happen. And there is no need for zero initialization.
Step 3.
But note that we(the compiler) still haven't initialized the variable d because it left its initialization because it has to be done dynamically. So now this will take place and d will get value 50. But note d still isn't a constant expression and hence we cannot use it where a constant expression is required.
Is my analysis/understanding of the concept correct and the code behaves as described?
Note:
The order of constant initialization and zero initialization is also different at cppref-init and enseignement-init.
When in doubt, turn to the standard. As this question is tagged with C++11, we'll refer to N3337.
[basic.start.init]/2:
Variables with static storage duration ([basic.stc.static]) or thread
storage duration ([basic.stc.thread]) shall be zero-initialized
([dcl.init]) before any other initialization takes place.
Constant initialization is performed: [...]
Together, zero-initialization and constant initialization are called
static initialization; all other initialization is dynamic
initialization. Static initialization shall be performed before any
dynamic initialization takes place.
Thus, with regard to the C++11 Standard, enseignement's description is accurate.
Constant initialization is performed after zero initialization of the static and thread-local objects and before all other initialization.
However, this was flagged as a defect as per CWG 2026:
CWG agreed that constant initialization should be considered as happening instead of zero initialization in these cases, making the declarations ill-formed.
And as of C++17 (N4659) this was changed, and henceforth governed by [basic.start.static]/2:
[...] Constant initialization is performed if a variable or temporary object with static or thread storage duration is initialized by a constant initializer for the entity. If constant initialization is not performed, a variable with static storage duration or thread storage duration is zero-initialized. Together, zero-initialization and constant initialization are called static initialization; all other initialization is dynamic initialization.
But as this was not just new standard feature update, but a defect, it backports to older standard, and in the end cppreference's description is accurate also for C++11 (in fact all the way back to C++98), whereas enseignement's has not taken into account neither modern C++ standard nor the DR CWG 2026.
I have never visited enseignement's page myself, but after a quick glance at their reference page, it looks like a really old version of cppreference itself, either entirely unattributed to cppreference, or cppreference actually started as a collab with the enseignement authors.
Short of the standard itself I consider cppreference to be a de facto reference, and given the old copy-pasta of enseignement I would recommend never to turn to those for pages reference again. And indeed, we may visit the actual up-to-date cppreference page on constant initialization, scroll to the very bottom and read:
Defect reports
The following behavior-changing defect reports were applied
retroactively to previously published C++ standards.
[...]
CWG 2026
Applied to: C++98
Behavior as published: zero-init was specified to always occur first, even before constant-init
Correct behavior: no zero-init if constant init applies

Are pointer to a member of this allowed in object initialization?

From Aggregate initialization, set pointer to struct member, is the following code legal:
struct S
{
int a;
int* aptr;
};
S s = { 3, &s.a };
Quote from latest standard draft:
[basic.scope.pdecl]
The point of declaration for a name is immediately after its complete declarator ([dcl.decl]) and before its initializer (if any), except as noted below.
So, yes. The identifier s has already been declared, so it can be used in its initialiser.
Note that the value of s may not be used until it has been initialised. The value is not used in the example, so this is not a problem.
I'd also be curious about whether analogous code is valid when the two members of S are in reversed order
The order of members does not matter.

Variable initialization, what's the difference? [duplicate]

This question already has answers here:
What are the advantages of list initialization (using curly braces)?
(5 answers)
Closed 3 years ago.
In my header, I have these two public variables:
float testVariable1 = 1.23456789f;
float testVariable2{ 1.23456789f };
What's the difference here?
Why have the version with the braces?
C++ supports three basic ways to initialize a variable.
First, we can do copy initialization by using an equals sign:
int width = 5; // copy initialization of value 5 into variable width
This copies the value on the right-hand side of the equals to the variable being created on the left-hand side.
Second, we can do a direct initialization by using parenthesis.
int width( 5 ); // direct initialization of value 5 into variable width
For simple data types (like integers), copy and direct initialization are essentially the same. But for some advanced types, direct initialization can perform better than copy initialization.
Before C++11, direct initialization was recommended over copy initialization in most cases because of the performance boost.
Unfortunately, direct initialization can’t be used for all types of initialization. In an attempt to provide a more consistent initialization mechanism, C++11 added a new syntax for direct initialization called brace initialization (also called uniform initialization) that uses curly braces:
int width{ 5 }; // brace (uniform) initialization of value 5 into variable width
Hope this will help you.
They have the same effect for this case.
The 1st one is copy initialization,
Otherwise (if neither T nor the type of other are class types), standard conversions are used, if necessary, to convert the value of other to the cv-unqualified version of T.
the 2nd one is direct-list-initialization.
Otherwise (if T is not a class type), if the braced-init-list has only one element and either T isn't a reference type or is a reference type whose referenced type is same as or is a base class of the type of the element, T is direct-initialized (in direct-list-initialization) or copy-initialized (in copy-list-initialization), except that narrowing conversions are not allowed.
and
Otherwise, standard conversions are used, if necessary, to convert the value of other to the cv-unqualified version of T, and the initial value of the object being initialized is the (possibly converted) value.
As build-in types, one of the potential differences is that narrowing conversions are not allowed in list initialization. (Even it won't be applied in this case because the initializers have been specified as float literals.)

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

Is it defined behavior to reference an early member from a later member expression during aggregate initialization?

Consider the following:
struct mystruct
{
int i;
int j;
};
int main(int argc, char* argv[])
{
mystruct foo{45, foo.i};
std::cout << foo.i << ", " << foo.j << std::endl;
return 0;
}
Note the use of foo.i in the aggregate-initializer list.
g++ 5.2.0 outputs
45, 45
Is this well-defined behavior? Is foo.i in this aggregate-initializer always guaranteed to refer to the being-created structure's i element (and &foo.i would refer to that memory address, for example)?
If I add an explicit constructor to mystruct:
mystruct(int i, int j) : i(i), j(j) { }
Then I get the following warnings:
main.cpp:15:20: warning: 'foo.a::i' is used uninitialized in this function [-Wuninitialized]
a foo{45, foo.i};
^
main.cpp:19:34: warning: 'foo.a::i' is used uninitialized in this function [-Wuninitialized]
cout << foo.i << ", " << foo.j << endl;
The code compiles and the output is:
45, 0
Clearly this does something different, and I'm assuming this is undefined behavior. Is it? If so, why the difference between this and when there was no constructor? And, how can I get the initial behavior (if it was well-defined behavior) with a user-defined constructor?
Your second case is undefined behavior, you are no longer using aggregate initialization, it is still list initialization but in this case you have a user defined constructor which is being called. In order to pass the second argument to your constructor it needs to evaluate foo.i but it is not initialized yet since you have not yet entered the constructor and therefore you are producing an indeterminate value and producing an indeterminate value is undefined behavior.
We also have section 12.7 Construction and destruction [class.cdtor] which says:
For an object with a non-trivial constructor, referring to any non-static member or base class of the object
before the constructor begins execution results in undefined behavior [...]
So I don't see a way of getting your second example to work like your first example, assuming the first example is indeed valid.
Your first case seems like it should be well defined but I can not find a reference in the draft standard that seems to make that explicit. Perhaps it is defect but otherwise it would be undefined behavior since the standard does not define the behavior. What the standard does tell us is that the initializers are evaluated in order and the side effects are sequenced, from section 8.5.4 [dcl.init.list]:
Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack
expansions (14.5.3), are evaluated in the order in which they appear. That is, every value computation and
side effect associated with a given initializer-clause is sequenced before every value computation and side
effect associated with any initializer-clause that follows it in the comma-separated list of the initializer-list. [...]
but we don't have an explicit text saying the members are initialized after each element is evaluated.
MSalters argues that section 1.9 which says:
Accessing an object designated by a volatile glvalue (3.10), modifying an object, calling a library I/O
function, or calling a function that does any of those operations are all side effects, which are changes in the
state of the execution environment. [...]
combined with:
[...]very value computation and side effect associated with a given initializer-clause is sequenced before every value computation and side effect associated with any initializer-clause that follows it [...]
Is sufficient to guarantee each member of the aggregate is initialized as the elements of the initializer list are evaluated. Although this would be not apply prior to C++11 since the order of evaluation of the initializer list was unspecified.
For reference if the standard does not impose a requirement the behavior is undefined from section 1.3.24 which defines undefined behavior:
behavior for which this International Standard imposes no requirements
[ Note: Undefined behavior may be expected when this International Standard omits any explicit definition of
behavior or [...]
Update
Johannes Schaub points out defect report 1343: Sequencing of non-class initialization and std-discussion threads Is aggregate member copy-initialization associated with the corresponding initializer-clause? and Is copy-initialization of an aggregate member associated with the corresponding initializer-clause? which are all relevant.
They basically point out that the first case is currently unspecified, I will quote Richard Smith:
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 although as indicated in several places it looks like current implementations do what we expect, it seems unwise to rely on it until this is officially clarified or the implementations provide a guarantee.
C++20 Update
With the Designated Initialization proposal: P0329 the answer to this question changes for the first case. It contains the following section:
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
We can see this is reflected in the latest draft standard
From [dcl.init.aggr] 8.5.1(2)
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.
emphasis mine
And
Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack expansions (14.5.3), are evaluated in the order in which they appear. That is, every value computation and side effect associated with a given initializer-clause is sequenced before every value computation and side effect associated with any initializer-clause that follows it in the comma-separated list of the initializer-list.
Leads me to believe that each member of the class will be initialized in the order they are declared in the initializer-list and since foo.i is initialized before we evaluate it to initialize j this should be defined behavior.
This is also backed up with [intro.execution] 1.9(12)
Accessing an object designated by a volatile glvalue (3.10), modifying an object, calling a library I/O function, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment.
emphasis mine
In your second example we are not using aggregate initialization but list initialization. [dcl.init.list] 8.5.4(3) has
List-initialization of an object or reference of type T is defined as follows:
[...]
- Otherwise, if T is a class type, constructors are considered. The applicable constructors are enumerated
and the best one is chosen through overload resolution (13.3, 13.3.1.7).
So now we would call your constructor. When calling the constructor foo.i has not been initialized so we are copying an uninitialized variable which is undefined behavior.
My first idea was UB, but you are fully in the aggregate initialization case. Draft n4296 for C++ 11 specification is explicit in the 8.5.1 Aggregates [dcl.init.aggr] paragraph:
An aggregate is an array or a class with no user-provided constructors , no private or protected non-static data members, no base classes, and no virtual functions
Later:
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
(emphasize mine)
My understanding is that mystruct foo{45, foo.i}; first initializes foo.i with 45, then foo.j with foo.i.
I would not dare to use that in real code anyway, because even if I believe it is defined by standard, I would be afraid that a compiler programmer has thought differently...
how can I get the initial behavior (if it was well-defined behavior) with a user-defined constructor?
Passing parameter by reference for that parameter which refers to previously initialized parameter of being constructed object, as follows:
mystruct(int i, int& j):i(i),j(j)