initialization sequence issue

  • Thread starter Christof Warlich
  • Start date
C

Christof Warlich

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() {}
 
S

Salt_Peter

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() {}

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!)
 
C

Christof Warlich

section 3.6.2
The storage for objects with static storage duration shall be zero
initialized before any other initilization takes place
ok, but in my case, both variables are explicitly initialized to
non-zero values.
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!)

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

Salt_Peter

ok, but in my case, both variables are explicitly initialized to
non-zero values.

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

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

Christof Warlich

Salt_Peter said:
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.

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

Rahul

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!)

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?
 
J

James Kanze

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

I'm not sure I'd have any expectations about this. Line 8
doesn't define anything.
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?

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

Note that the above is a declaration, not a definition.
9 X &x = A<X>::t;
10 int d = printf("initialized d\n");
11 int main() {}

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

Christof Warlich

James said:
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.
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.
Just curious, but what do you expect to happen if A<X>::t is
also implicitely initialized in another translation unit?
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!
I think you've got a case of unspecified behavior here.
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.
 
C

Christof Warlich

Rahul said:
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?
Absolutely right, that's my understanding as well.
 
J

James Kanze

James Kanze schrieb:
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>;

Which I don't think changes anything.
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.

Well, it obviously has to throw away all but one. Which one is
unspecified.
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.

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.
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!> I think you've got a case of
unspecified behavior here.

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

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.
 

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

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,011
Latest member
AjaUqq1950

Latest Threads

Top