array initialiser list, order of initialisation

K

kwikius

Hi,

In the following code, the initialisation of the array elements depends on
the first in the initialiser list being initialised before the second and so
on. Can I rely on that?


#include <iostream>

int main()
{
int ar[3] = {
1,
ar[0]+1,
ar[1]+1
};

std::cout
<< ar[0] << ' '
<< ar[1] << ' '
<< ar[2] << '\n';

}

regards
Andy Little
 
I

Ivan Novick

Hi,

In the following code, the initialisation of the array elements depends on
the first in the initialiser list being initialised before the second and so
on. Can I rely on that?

#include <iostream>

int main()
{
  int ar[3] = {
    1,
    ar[0]+1,
    ar[1]+1
  };

  std::cout
  << ar[0] << ' '
  << ar[1] << ' '
  << ar[2] << '\n';

}
I don't see anything in the standard in section 8.5.1 about
initializing aggregates, that indicates the order of the initilization
is guaranteed by the order of the items in the initializer list.
Since its not explicitly called out in the standard, I guess that
means it may or may not work on any given platform. Basically as Alf
said, forget about this syntax.

Ivan Novick
http://www.mycppquiz.com/
 
J

joseph cook

Hi,

In the following code, the initialisation of the array elements depends on
the first in the initialiser list being initialised before the second and so
on. Can I rely on that?

#include <iostream>

int main()
{
  int ar[3] = {
    1,
    ar[0]+1,
    ar[1]+1
  };

Prefer putting this in your toolbox:
template<typename T>
class RampGen
{
public:
RampGen(T init=0, T inc=1) : m_data(init), m_incrementor(inc) {}
T operator()() { T result = m_data; m_data += m_incrementor; return
result; }
T m_incrementor;
T m_data;
};

and then just use:
std::vector<int> vec(3);
std::generate(vec.begin(),vec.end(),RampGen(1));

Joe Cook
 
K

kwikius

Since you have to ask, you shouldn't.

I'm too lazy to dive into the standard to support some ungood coding for
you.

I suggest you do that yourself, or better, forget about such ungood
things.

hmm... thats a shame, cos I like it! :)

regards
Andy Little
 
J

James Kanze

In the following code, the initialisation of the array
elements depends on the first in the initialiser list being
initialised before the second and so on. Can I rely on that?
#include <iostream>
int main()
{
int ar[3] = {
1,
ar[0]+1,
ar[1]+1
};
std::cout
<< ar[0] << ' '
<< ar[1] << ' '
<< ar[2] << '\n';
}
I don't see anything in the standard in section 8.5.1 about
initializing aggregates, that indicates the order of the
initilization is guaranteed by the order of the items in the
initializer list. Since its not explicitly called out in the
standard, I guess that means it may or may not work on any
given platform.

There are actually two separate issues to be considered. First,
I'm pretty sure that there is a statement in the standard which
guarantees that arrays are initialized "in order" (and
destructed in the reverse order). So he's probably safe in that
regard. The second point is, of course, is there a sequence
point between the evaluation of the initialization expressions;
I think each counts as a "complete expression", and the answer
is yes, but I'm not sure.
Basically as Alf said, forget about this syntax.

I certainly agree there. As Alf said, if you have to ask...
 
K

kwikius

I certainly agree there. As Alf said, if you have to ask...

FWIW I think its valid.

The useage is for a graph showing the piecewise integral of a function of
some input.

Anway .. I like it and from what you say I have a strong hunch now that its
not a problem.

regards
Andsy Little
 
J

Jerry Coffin

Hi,

In the following code, the initialisation of the array elements depends on
the first in the initialiser list being initialised before the second and so
on. Can I rely on that?


#include <iostream>

int main()
{
int ar[3] = {
1,
ar[0]+1,
ar[1]+1
};

std::cout
<< ar[0] << ' '
<< ar[1] << ' '
<< ar[2] << '\n';

}

As far as I can see, no. IF this used dynamic initialization, you'd be
guaranteed that the objects in the array would be initialized in order,
and you'd be guaranteed that there was a sequence point between each
initialization and the next, so you'd get defined results.

What you have, however, is an array of items (ints) with no user-
declared constructors, no private or protected non-static members, no
base classes and no virtual functions. That means what you have is an
aggregate, which is static initialized.

The only guarantee made about order of static initialization is that it
happens before dynamic initialization. As such, your code currently has
undefined behavior -- it could work for some compilers, and fail for
others, or change behavior based on the compilation flags you use, or
whatever.

It is, however, trivial to make it work correctly: instead of int's,
create an array of proxy objects that act like ints:

class Int {
int value;
public:
Int(int v) : value(v) {}
operator int &() { return value; }
};

Int ar[3] = {
1,
ar[0] + 1,
ar[1] + 1
};

Since this has a user-declared ctor, it's not an aggregate. Since it's
not an aggregate, you get dynamic initialization instead of static
initialization. Dynamic initialization guarantees that the
initialization happens in order, with a sequence point between each
initialization and the next. IOW, it works.

As far as speed goes, I'm reasonably certain any decent compiler will
produce identical code for manipulation thsese objects as it would for
raw ints. For example, given code like:

int total = 0;

for (int i=0; i<3; i++)
total += ar;

There is no difference between the code produced if ar is an array of
int or of Int. Of course, that theoretically applies only to the
compilers I tested -- in theory, some other compiler _could_ produce
different code, though I'd be rather surprised to see it. Offhand, I
can't really think of what it _could_ reasonably change...
 
J

James Kanze

In the following code, the initialisation of the array
elements depends on the first in the initialiser list being
initialised before the second and so on. Can I rely on that?
#include <iostream>
int main()
{
int ar[3] = {
1,
ar[0]+1,
ar[1]+1
};
std::cout
<< ar[0] << ' '
<< ar[1] << ' '
<< ar[2] << '\n';
}
As far as I can see, no. IF this used dynamic initialization,
you'd be guaranteed that the objects in the array would be
initialized in order, and you'd be guaranteed that there was a
sequence point between each initialization and the next, so
you'd get defined results.

Could you remind me where this is specified. I was looking for
it, but I couldn't find it.

More generally, I'm pretty sure that if you write something
like:

void
f()
{
T arr[ 5 ] ;
}

it is guaranteed that the constructors of arr are called "in
order", and that if one exits with an exception, the destructors
of the already constructed objects are called in the reverse
order, but I can't find this either.
What you have, however, is an array of items (ints) with no
user- declared constructors, no private or protected
non-static members, no base classes and no virtual functions.
That means what you have is an aggregate, which is static
initialized.

What he has is a variable with automatic lifetime, which can't
have static initialization. Even at namespace scope, it would
have static initialization, because of the array accesses. All
of the three compilers I have access to agree, and use dynamic
initialization.

(Whether static initialization or dynamic is involved can easily
be tested with something like:

int f() ;

int const i = f() ;
int const arr[ 3 ] = { 0, arr[0]+1, arr[1]+1 } ;

int
f()
{
return arr[2] ;
}

The initialization of i is dynamic. If the initialization of
arr is static, it takes place before the call to f() in the
initialization of i, and i is initialized with 2. If it is
dynamic, it takes place after, and i is initialized with 0.)
The only guarantee made about order of static initialization
is that it happens before dynamic initialization.

The requirements for static initialization have been carefully
formulated so that the initialization value can be determined by
the compiler. Static initialization requires all of the
initializers to be constants, and according to the current
standard, "An integral constant-expression can involve only
literals, enumerators, const variables or static data member of
integral or enumeration types initialized with constant
expressiosn, non-type template parameters of integral or
enumeration types, and sizeof expressions." Other types of
constant expressions are even more limited. In no case can a
constant expression contain an array access (which involves an
object of array type, which of course isn't an integral type).
As such,
your code currently has undefined behavior -- it could work
for some compilers, and fail for others, or change behavior
based on the compilation flags you use, or whatever.

I'm not sure that it's undefined behavior, although it's really
not very clear. There's clearly a sequence point between the
evaluation of each initializer expression, since each is a
complete expression. However, the initialization itself isn't
part of the expression, so that's not really sufficient.

It's an interesting question, actually. Suppose a type T, with
a constructor which takes an int, and something like the
following:

int
f( int i )
{
std::cout << i << std::endl ;
return i ;
}

main()
{
T arr[ 3 ] = { f( 1 ), f( 2 ), f( 3 ) } ;
}

As I said above, I'm pretty sure that the constructors must be
called in the order arr[0], arr[1], arr[2]. And f(1), must be
called, and all of its side effects must occur, before f(2) is
called. But I don't think that there's anything which
prevents an order like f(1), f(2), f(3), ctor(arr[0]),
ctor(arr[1]), ctor(arr[2]) (which could make a difference if f
had side effects, and one of the constructors threw).
It is, however, trivial to make it work correctly: instead of
int's, create an array of proxy objects that act like ints:
class Int {
int value;
public:
Int(int v) : value(v) {}
operator int &() { return value; }
};
Int ar[3] = {
1,
ar[0] + 1,
ar[1] + 1
};
Since this has a user-declared ctor, it's not an aggregate.
Since it's not an aggregate, you get dynamic initialization
instead of static initialization. Dynamic initialization
guarantees that the initialization happens in order, with a
sequence point between each initialization and the next. IOW,
it works.

He's got dynamic initialization already, so any guarantees which
apply to dynamic initialization apply here. But I'm not sure
that the behavior is defined with dynamic initialization,
either. (And as I say, although I'm sure they exists, I can't
find the guarantees concerning the order of array elements in
dynamic initialization. And the exact wording matters in this
case.)
 
A

Andrew Koenig

I don't see anything in the standard in section 8.5.1 about
initializing aggregates, that indicates the order of the initilization
is guaranteed by the order of the items in the initializer list.

Actually, the issue is not so much the order of element initialization as it
is the order in which the initializers themselves are evaluated.

If I write

int a[2] = { f(), g() };

is the implementation permitted to evaluate f(), then evaluate g(), then
store the result of f() in a[0], and finally store the result of g() in
a[1]? I don't see any reason why not.
 
J

Jerry Coffin

[ ... order of dynamic initialization ]
Could you remind me where this is specified. I was looking for
it, but I couldn't find it.

3.6.2/1, at least by my reading. What it says could be read as not
really applying in this case though. It says: "Objects with static
storage duration defined in namespace scope in the same translation
unit and dynamically initialized shall be initialized in the order in
which their definition appears in the translation unit."

As you pointed out, however, that doesn't really apply in this case,
because in his code it's a local object, that doesn't have static
storage duration. Given that it's an automatic variable, the relevant
language (to the extent that it exists at all) is in 6.7/4: "Otherwise
such an object is initialized the first time control passes through its
declaration; such an object is considered initialized upon the
completion of its initialization."

In neither case does it explicitly specify the order of initialization
of element in an array. I'd take the "order of definition" of elements
in an array as being their order in the array, but I'll admit that's
never really stated directly.
More generally, I'm pretty sure that if you write something
like:

void
f()
{
T arr[ 5 ] ;
}

it is guaranteed that the constructors of arr are called "in
order", and that if one exits with an exception, the destructors
of the already constructed objects are called in the reverse
order, but I can't find this either.

This hinges on the same language above. All that's added is the
destructors being called in reverse order, which is specified in 6.6/2:
"On exit from a scope (however accomplished), destructors (12.4) are
called for all constructed objects with automatic storage duration
(3.7.2) (named objects or temporaries) that are declared in that scope,
in the reverse order of their declaration."

As such, if elements in an array are considered defined in their order
within the array, it does. Otherwise it doesn't. If there were such
language, it should be somewhere in 8.3.4, but I can't find such a
thing.

[ ... ]
He's got dynamic initialization already, so any guarantees which
apply to dynamic initialization apply here. But I'm not sure
that the behavior is defined with dynamic initialization,
either. (And as I say, although I'm sure they exists, I can't
find the guarantees concerning the order of array elements in
dynamic initialization. And the exact wording matters in this
case.)

Quite true.
 
J

James Kanze

Actually, the issue is not so much the order of element
initialization as it is the order in which the initializers
themselves are evaluated.
If I write
int a[2] = { f(), g() };
is the implementation permitted to evaluate f(), then evaluate
g(), then store the result of f() in a[0], and finally store
the result of g() in a[1]? I don't see any reason why not.

Because they're both complete expressions, and so there are
sequence points involved. But I'll admit that I don't know
quite what they are ordering.
 
K

kwikius

Andrew Koenig said:
I don't see anything in the standard in section 8.5.1 about
initializing aggregates, that indicates the order of the initilization
is guaranteed by the order of the items in the initializer list.

Actually, the issue is not so much the order of element initialization as
it is the order in which the initializers themselves are evaluated.

If I write

int a[2] = { f(), g() };

is the implementation permitted to evaluate f(), then evaluate g(), then
store the result of f() in a[0], and finally store the result of g() in
a[1]? I don't see any reason why not.

Thinking about it, the main reason I can think of for leaving the order of
initialisation open might be to allow parallel initialisation, e.g. loading
the array into a vector register, barring that it would seem to make no
sense to leave a temporary somewhere when stack storage has been allocated.
OTOH As it stands AIUI paralleism is not really viable at the micro level,
due to shared memory single register hardware model and of course C++ is
very much tied to sequential model of execution, but I can sort of visualise
an architecture where an array can be stored in a vector register in one
cycle as I believe happens as a matter of course in Graphics coprocessors
etc. Quite how C++ fits in to that type of model I don't know though ...

regards
Andy Little
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,754
Messages
2,569,521
Members
44,995
Latest member
PinupduzSap

Latest Threads

Top