array initialiser list, order of initialisation

Discussion in 'C++' started by kwikius, Jul 10, 2008.

  1. kwikius

    kwikius Guest

    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
    kwikius, Jul 10, 2008
    #1
    1. Advertising

  2. kwikius

    Ivan Novick Guest

    On Jul 10, 3:03 pm, "kwikius" <> wrote:
    > 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/
    Ivan Novick, Jul 11, 2008
    #2
    1. Advertising

  3. kwikius

    joseph cook Guest

    On Jul 10, 6:03 pm, "kwikius" <> wrote:
    > 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
    joseph cook, Jul 11, 2008
    #3
  4. kwikius

    kwikius Guest

    "Alf P. Steinbach" <> wrote in message
    news:...

    > 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
    kwikius, Jul 11, 2008
    #4
  5. kwikius

    James Kanze Guest

    On Jul 11, 2:22 am, Ivan Novick <> wrote:
    > On Jul 10, 3:03 pm, "kwikius" <> wrote:


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

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Jul 11, 2008
    #5
  6. kwikius

    kwikius Guest

    "James Kanze" <> wrote in message
    news:...
    On Jul 11, 2:22 am, Ivan Novick <> wrote:


    > > Basically as Alf said, forget about this syntax.


    > 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
    kwikius, Jul 11, 2008
    #6
  7. kwikius

    Jerry Coffin Guest

    In article <48768727$>,
    says...
    > 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...

    --
    Later,
    Jerry.

    The universe is a figment of its own imagination.
    Jerry Coffin, Jul 11, 2008
    #7
  8. kwikius

    James Kanze Guest

    On Jul 11, 5:11 pm, Jerry Coffin <> wrote:
    > In article <48768727$>,
    > says...
    > > 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.)

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Jul 11, 2008
    #8
  9. "Ivan Novick" <> wrote in message
    news:...

    > 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.
    Andrew Koenig, Jul 11, 2008
    #9
  10. kwikius

    Jerry Coffin Guest

    In article <f677163f-6ce3-4f04-b83c-8e0854cda2ad@
    26g2000hsk.googlegroups.com>, says...

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

    --
    Later,
    Jerry.

    The universe is a figment of its own imagination.
    Jerry Coffin, Jul 11, 2008
    #10
  11. kwikius

    James Kanze Guest

    On Jul 11, 6:18 pm, "Andrew Koenig" <> wrote:
    > "Ivan Novick" <> wrote in message


    > news:...


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


    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.

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
    James Kanze, Jul 11, 2008
    #11
  12. kwikius

    kwikius Guest

    "Andrew Koenig" <> wrote in message
    news:AJLdk.112031$...
    > "Ivan Novick" <> wrote in message
    > news:...
    >
    >> 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
    kwikius, Jul 11, 2008
    #12
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. John Harrison
    Replies:
    2
    Views:
    1,253
    =?iso-8859-1?Q?Juli=E1n?= Albo
    Jun 25, 2003
  2. John Carson
    Replies:
    2
    Views:
    325
    John Carson
    Jan 23, 2004
  3. clilley
    Replies:
    6
    Views:
    366
    Howard Hinnant
    Nov 18, 2004
  4. Tim Clacy
    Replies:
    8
    Views:
    382
    Tim Clacy
    May 30, 2006
  5. Olaf P.

    Array of Array Initialisation

    Olaf P., Apr 24, 2007, in forum: C Programming
    Replies:
    2
    Views:
    314
    Duncan Muirhead
    Apr 24, 2007
Loading...

Share This Page