Chris Johnson
Chris Johnson

Reputation: 10680

C++ initialisation: order of const global vs static class member

In the following library code:

#include <cmath>

namespace blah {
const double pi=4.0*std::atan(1.0);
}

template <int I>
class ClassB
{
public:
   ClassB() {myval = blah::pi;}
private:
   double myval;
};

template <int I>
class ClassA
{
public:
   static ClassB<I> b;
};

template<int I>
ClassB<I> ClassA<I>::b = ClassB<I>();

template class ClassA<3>;

is the variable pi guaranteed by the standard to be assigned to its value 4.0*std::atan(1.0) before the constructor of ClassB uses it?

As far as I can tell from the standard, pi and static ClassA<I>::ClassB<I> b should be initialised in the order in which they are defined in this single translation unit - and thus pi should be initialised first.

However, in my real codebase with the code structure as above, I am finding that in code compiled by Clang 3.6, pi is equal to zero at the time that the ClassB constructor is executed, and is initialised to its proper value only after this. (GCC 4.8.3 initialises pi first, as I was expecting.)

Upvotes: 2

Views: 96

Answers (2)

Chris Johnson
Chris Johnson

Reputation: 10680

As James Kanze says, the answer is no, the order cannot be guaranteed (even though ClassA<3> is explicitly instantiated within the translation unit). This answer provides more detail on the problem.

One solution is to is to specialise the static member variable in the library .cpp file, with

template<>
ClassB<3> ClassA<3>::b = ClassB<3>();

rather than instantiating the template with template class ClassA<3>;.

The C++03 spec. doesn't spell this out explicitly, but the C++11 spec. is clearer on the issue:

Definitions of explicitly specialized class template static data members have ordered initialization. Other class template static data members (i.e., implicitly or explicitly instantiated specializations) have unordered initialization.

Upvotes: 0

James Kanze
James Kanze

Reputation: 154047

The simple answer is no. There are no guarantees. If ClassB<I> ClassA<I>::b = ClassB<I>() were not a template, there would be a guarantee, since both are in the same translation unit, but that guarantee ceases to exist if the static is member of a template class (presumably because the actual instantiation could be in any translation unit).

In this particular case, however: why the convoluted way of getting a constant pi. Just:

double const pi = 3.1415926535897932384626433832795;

should be enough. (If you've any doubts for any of the target machines, then add more digits. But this is more than is needed to get the most accurate representation possible for IEEE.) And because this is static initialization, it is guaranteed to occur before any dynamic initialization takes place.

Upvotes: 3

Related Questions