Question

I have two templated structures that each contain a const static member variable. The initialization of one of these member variables depends on the second. I would therefore like to be able to guarantee that the second is initialized before the first. Here's a simplified example:

dependency.hpp:

template<typename T, T> struct value { };

template <typename T>
struct Dependency {
  Dependency() {}
  Dependency(T v) : var(v) {}
  const static Dependency staticVar;
  T var;
};

template <typename T>
const Dependency<T> Dependency<T>::staticVar = Dependency<T>(1.5);

testStruct.hpp:

#include "dependency.hpp"

//template class Dependency<double>; [1]
//template class Dependency<float>;  [2]

template <typename T>
struct TestStruct {
  TestStruct(Dependency<T> v) : var(v.var) {}

  const static TestStruct staticVar;
  T var;
};

template <typename T>
const TestStruct<T> TestStruct<T>::staticVar = TestStruct<T>(Dependency<T>(Dependency<T>::staticVar));

test.cpp:

#include <iostream>
#include "testStruct.hpp"

using namespace std;

int main(int argc, char *argv[])
{
  cout << "TestStruct<d> " << TestStruct<double>::staticVar.var << endl;
  cout << "Dependency<d> " << Dependency<double>::staticVar.var << endl;

  cout << endl;

  cout << "Dependency<f> " << Dependency<float>::staticVar.var << endl; // [3]
  cout << "TestStruct<f> " << TestStruct<float>::staticVar.var << endl;

  return 0;
};

The output of main is

TestStruct<d> 0
Dependency<d> 1.5

Dependency<f> 1.5
TestStruct<f> 1.5

That is, the TestStruct<T>'s staticVar gets correctly initialized if Dependency has already been instantiated for type T, but it remains at 0 otherwise, since Dependency<T>::staticVar hasn't yet been initialized. Uncommenting [1] and [2] solves the problem for types float and double (i.e., everything outputs 1.5, even with [3] commented), but I would prefer if possible not to have to list all possible types or to instantiate the template for those types in code that does not use them. I would like to be able to put something in TestStruct<T> (or testStruct.hpp) to guarantee that Dependency has been instantiated for that type without having to specify what types T may be.

I have seen C++ Static member initalization (template fun inside) and How to force a static member to be initialized?. The first explains the situation well, but does not propose a solution for a problem like mine. The second has two solutions, but neither appears to work in GCC 4.2.1 (or I applied it incorrectly...).

Are there any other tricks or work-arounds I should try, or am I stuck with explicit instantiation?

Was it helpful?

Solution

As suggested by cdhowie, you can use a static method to ensure initialization order between TestStruct and Dependency. If you are really concerned about maintaining the appearance of a static variable rather than a static method, you can use a static reference variable that is initialized by the static method call.

You only need to ensure that your static method implementations do not themselves use the static reference variables, so that they can be safely called during the global initialization context.

template <typename T>
struct Dependency {
  Dependency() {}
  Dependency(T v) : var(v) {}
  static const Dependency & staticMethod () {
      static const Dependency staticMethodVar(1.5);
      return staticMethodVar;
  }
  static const Dependency & staticVar;
  T var;
};

template <typename T>
const Dependency<T> & Dependency<T>::staticVar
        = Dependency<T>::staticMethod();

template <typename T>
struct TestStruct {
  TestStruct(Dependency<T> v) : var(v.var) {}
  static const TestStruct & staticMethod () {
      static const TestStruct staticMethodVar(Dependency<T>::staticMethod());
      return staticMethodVar;
  }
  static const TestStruct & staticVar;
  T var;
};

template <typename T>
const TestStruct<T> & TestStruct<T>::staticVar
        = TestStruct<T>::staticMethod();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top