initialization sequence issue

Discussion in 'C++' started by Christof Warlich, Jan 5, 2008.

  1. Hi,

    the few lines of code below show different results depending on
    the compiler version (gcc-2.95 versus gcc-3.3 and later):

    gcc-2.95 first initializes d (line 9) and then t (line 8), as
    running the executable yields

    initialized d
    X constructor

    whereas gcc-3.3 and later gcc versions do as I'd expect: They
    initialize the variables as they appear in the source code:

    X constructor
    initialized d

    Could anyone tell if the initialization sequence is specified
    by the standard in such a case or if I cannot rely on the
    initialization sequence even when everything is in the same translation
    unit?

    Thanks,

    Christof

    1 #include <stdio.h>
    2 struct X {
    3 X() {printf("X constructor\n");}
    4 };
    5 template<typename T> struct A {
    6 static T t;
    7 };
    8 template<typename T> T A<T>::t;
    9 X &x = A<X>::t;
    10 int d = printf("initialized d\n");
    11 int main() {}
     
    Christof Warlich, Jan 5, 2008
    #1
    1. Advertisements

  2. Christof Warlich

    Salt_Peter Guest

    section 3.6.2
    The storage for objects with static storage duration shall be zero
    initialized before any other initilization takes place

    section 3.7.1 says
    The keyword static applied to a class data member in a class
    definition gives the data member static storage duration.
    (does not cover those static members in a namespace scope!)
     
    Salt_Peter, Jan 5, 2008
    #2
    1. Advertisements

  3. ok, but in my case, both variables are explicitly initialized to
    non-zero values.
    What I really like to know is if I usually could rely on having
    my global variables being initialized in the same sequence as
    they appear in the source code, i.e. whether the old gcc-2.95
    compiler is buggy because it initializes d _before_ t or if this
    is an implementation detail that is not defined by the standard.
     
    Christof Warlich, Jan 5, 2008
    #3
  4. Christof Warlich

    Salt_Peter Guest

    That shouldn't change the result of when that member is initialized.
    I was just pointing out that the above section indicates that
    initialization takes place before those variables having other storage
    durations. I'm trying to explain what the standard says in sequence.
    3.6.2 is about static storage duration.
    3.7.1 says that a static member, as long as its not in a namespace,
    has static storage duration.
    The standard therefore guarentees that the static member is treated as
    in 3.6.2, so gcc-2.95 appears to be non-compliant in that respect.
    I'ld suggest that its not something that is implementation defined.
     
    Salt_Peter, Jan 6, 2008
    #4
  5. Thanks a lot for this precise answer, it will help me to convince my
    colleagues from the build-team that their (hand-made) way of collecting
    static ctors is not according to the standard.
     
    Christof Warlich, Jan 6, 2008
    #5
  6. Christof Warlich

    Rahul Guest

    But aren't global variables static by default? static for a global
    variable just reduces the visibility scope of the global variable to
    that particular compilation unit, isn't it?
     
    Rahul, Jan 6, 2008
    #6
  7. Christof Warlich

    James Kanze Guest

    I'm not sure I'd have any expectations about this. Line 8
    doesn't define anything.
    The standard guarantees that object definitions requiring
    dynamic initialization occur in the order of the definitions
    within a given translation unit. Note, however, that a static
    member of a template is only "defined" if it is
    instantiated---each instantiation is a separate definition (and
    the template definition is *not* a definition of an object).
    Because the point of instantiation will vary if the static
    member is instantiated in several different translation units,
    the standard simply says that the order in this case is
    unspecified.
    Note that the above is a declaration, not a definition.
    Just curious, but what do you expect to happen if A<X>::t is
    also implicitely initialized in another translation unit?

    I think you've got a case of unspecified behavior here.
     
    James Kanze, Jan 6, 2008
    #7
  8. You are right, this is a declaration. And my initial posting was
    off by one line: I meant to talk about lines 9 and 10 respectively,
    which _are_ both definitions.
    But anyway, as I introduced line 9 only to instantiate the template
    to run the X constructor, it should be replaced by

    template struct A<X>;

    But anyway, the result is still the same with the two compiler
    versions.
    Well, this is an interesting question, as I was paying arround with this
    just exactly because I intentionally wanted to instantiate it within
    other translation units: When I do this, the linker seems to silently
    through away all instantiations but the very first it finds in any of
    the translation units that it links. Thus, this (together with the
    improvement that I've described above) seems to be a method to _define_
    variables in header files, even if they are included into multiple
    translation units. This may be useful for libraries like the STL that
    entirely live in header files, as it allows to work around the need
    for a separate translation unit just because the library needs some
    global variables. I'd be very interested to get some feedback on this
    idea!
    Well, as I said, lines 9 and 10 are the ones of interest, with line
    9 replaced as suggested above. And I still hope that the sequence of
    these lines define the sequence of construction of the related variables.
     
    Christof Warlich, Jan 6, 2008
    #8
  9. Absolutely right, that's my understanding as well.
     
    Christof Warlich, Jan 6, 2008
    #9
  10. Christof Warlich

    James Kanze Guest

    Which I don't think changes anything.
    Well, it obviously has to throw away all but one. Which one is
    unspecified.
    It could be. But it raises the problem you're complaining
    about: the variable is defined in many different translation
    units, so its order of initialization with respect to any one
    translation unit is unspecified.
    I believe that Matt Austern used a similar trick at some time,
    precisely to enable a library to be delivered as header files
    only.

    The order of initialization is unspecified. The rest of the
    behavior is well defined. When using such a trick, I would
    strongly recommend ensuring that the initialization is static.
    They impose the order of initialization of x and d: you can be
    assured that x is initialized to refer to A<X>::t before the
    initializer of d is called. You have no guarantee, however,
    that A<X>::t has been constructed; as a template instantiation,
    the order of initialization is unspecified. You end up with
    something more or less like:

    extern int a ;
    int& b = a ;
    int c = (std::cout << "initialized c" << std::endl, b ) ;

    Initializing a reference does not guarantee that what it refers
    to is already initialized.
     
    James Kanze, Jan 7, 2008
    #10
    1. Advertisements

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 (here). After that, you can post your question and our members will help you out.