views:

368

answers:

2

Within the same compilation unit, the C++ standard says that static initialization order is well defined -- it's the order of the declarations of the static objects. But using the Sun Studio 12 compiler I'm encountering unintuitive behavior. I've define a templated class helper<T> which contains a static member _data of type T and a static member function that uses _data called foo. In my .cpp file I have this above main():

struct A { /* some definition */ };

typedef helper<int> s0;
typedef helper<A> s1;

Notice that the typedef for helper<int> comes before the typedef for helper<A>. Thus according to the standard I would expect that helper<int>::_data will be constructed before helper<A>::_data (remember _data is a static member). On GCC this is the case, on Sun it is not.

This is problematic because A's constructor uses helper<int>::_data. I only have one compilation unit, with no earlier potential instantiation of helper<A>, so I thought the order should be well defined. Is this a Sun compiler bug, or does the typedef not constitute a definition/instantiation technically? What I mean is, is the Sun compiler's behavior allowed by the standard?

I have the following main():

int main()
{
    //Swapping the order of these has no effect on Sun
    s0::foo();
    s1::foo();
}

There are no other uses of s0 or s1.

A: 

You're not actually declaring any objects in that code.

You need extra code:

s0 one;
s1 two;

In that case, the two objects are now actually declared, and should work correctly.

Are you explicitly declaring a s0?

Try following the typedefs with a s0 dummy; and see if the problem is resolved.

Dave Gamble
From my understand the code *does* declare objects. helper<int> has a static data member. So every time that template is instantiated, a static data member is created (an object). The typedef's instantiate the template.... right?
Joseph Garvin
The typedef does not instantiate the template.
Dave Gamble
The typedef behaves, essentially, like a #define.
Dave Gamble
+6  A: 

Within the same compilation unit, the C++ standard says that static initialization order is well defined -- it's the order of the declarations of the static objects.

In your shown code you have no declaration of a static data member. You have a declaration of a typedef-name. These have nothing to do with that, and don't influence any order. You probably think along this way:

If i make that typedef declaration, it will instantiate helper<int>, and thus instantiate its static data member declaration first.

The problem is, that line does not cause an instantiation of helper<int>. For that to happen, you would need an explicit instantiation or manage to make it instantiate it implicitly (creating an object of helper<int> for example, or using it as a nested name specifier as in helper<int>::... and explicitly referencing the static member - otherwise, creation of it is omitted).

But there is a much deeper problem. The order is not the declaration of the static data-members. The order is their definition. Consider the following

struct C { C() { printf("hey\n"); } };
struct A { 
  static C a;
  static C b;
};

C A::b;
C A::a;

In this code, b is created before a, even though a is declared before b.

The following code prints 2 1:

struct C { C(int n) { printf("%d\n", n); } };

template<int N>
struct A {
  static C c;
};

template<int N>
C A<N>::c(N);

// explicit instantiation of declaration and definition
template struct A<2>;
template struct A<1>;

int main() {

}

But the following code prints nothing, unless you comment in the line in main.

struct C { C(int n) { printf("%d\n", n); } };

template<int N>
struct A {
  static C c;
};

template<int N>
C A<N>::c(N);

// implicit instantiation of declarations
A<2> a2;
A<1> a1;

int main() {
  // A<1>::c; A<2>::c;
}

I'm not actually sure what the correct output for this second snippet is. Reading the Standard, i can't determine an order. It says at 14.6.4.1 "Point of Instantiation":

For a function template specialization, a member function template specialization, or a specialization for a member function or static data member of a class template, if the specialization is implicitly instantiated because it is referenced from within another template specialization [...]. Otherwise, the point of instantiation for such a specialization immediately follows the namespace scope declaration or definition that refers to the specialization.

The point of instantiation of their definitions both appear immediately after the definition of main. Which definition is instantiated before the other definition seems to be left unspecified. If anyone knows the answer and khow other compilers behave (GCC prints 1 2 but with the order of the expressions in main swapped, prints 2 1), please let me know in the comment.

For details, see this answer about static object's lifetime.

Johannes Schaub - litb
Thanks for the thorough, informative answer.
Drew Hoskins
Unfortunately, if your answer is correct and I understand you correctly, then I think the Sun compiler is still wrong. In the code following the original snippet, s1::something_using_the_static_member was used before s0::something_using_the_static_member. I switched the order, but Sun still constructs s1's static member before s0's. Is my thinking correct?
Joseph Garvin
See my edit of original post
Joseph Garvin
Err, nevermind you acknowledge you're not sure what the order is in this case, I misread. The explicit instantiation does work. Maybe time to write a macro...
Joseph Garvin
For a class template, the point of instantiation is immediately *before* main (because a class type must be complete, it cannot come after). So, doing `s0::foo();` in main will instantiate `s0` implicitly immediately before main. And in this case, the context that `_data` is referenced from depends on a template parameter (it's after all inside the template's member function) so the instantiation point of `_data` is the same as the one for `s0`. But this suffers from the same problem: I dunno what the order of instantiation of `s0` and `s1` is. Mostly it doesn't make a difference...
Johannes Schaub - litb
... because the ODR (one definition rule) says that in the program, each instantiation of a template specialization may not have different behavior from one another. But here, we only have one specialization for `s0` and `s1` respectively, which have different results depending on their order. :(
Johannes Schaub - litb
I think i will ask this on usenet let's see what the guys there say about that. Maybe i'm all-wrong about everything i wrote above =). Btw this rule that where _data is instantiated in case it's referenced from within s0 is in the part that i have erased out with [...] because i thought it wasn't important for my example. But i realize now it is important for your example :)
Johannes Schaub - litb
Can you give me a link to your post once you make it? I'd like to see the responses too :)
Joseph Garvin
"Otherwise, the point of instantiation for such a specialization immediately follows the namespace scope declaration or definition that refers to the specialization." <-- thinking about this more, I don't see how it makes sense. This seems to say if a function uses a specialization that the specialization gets instantiated after the function! But that instantiation needs to exist already for the function to work. Confused @_@
Joseph Garvin
Only the declaration of it needs to exist. When it says "the static data member is instantiated..." it talks about the definition (which provides that entity). It is another case for classes: These are instantiated before the namespace scope declaration/definition that refers to them (that's why "s0" is instantiated *before* main), because, consider for example this: `sizeof(s0);` that actually needs the definition of s0. But this problem does not occur for functions/static data members: They may be defined after their use.
Johannes Schaub - litb
Oh I see now, it's just the definitions of static class template members that are put after the enclosing namespace scope. Jeeze...
Joseph Garvin
@Joseph, here is the link to the usenet thread: http://tinyurl.com/nsk2eq (tinyurl, because i found SO doesn't handle these links correctly sometimes)
Johannes Schaub - litb