Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Static field initialization order

Is there any trap in static constant field initialization order?

template <typename T>
struct constant_test {

    static const T PI;
    static const T FULL_CIRCLE;
    static const T HALF_CIRCLE;
    static const T DEG_TO_RAD;

};

template <typename T> const T constant_test<T>::PI = 3.141592653589f;
template <typename T> const T constant_test<T>::FULL_CIRCLE = 360.0f;
template <typename T> const T constant_test<T>::HALF_CIRCLE = constant_test<T>::FULL_CIRCLE / 2;
template <typename T> const T constant_test<T>::DEG_TO_RAD = constant_test<T>::PI / constant_test<T>::HALF_CIRCLE;

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {
    // uncomment to make it work
    // float test_ref = constant_test<float>::HALF_CIRCLE;
    char buf[128];
    sprintf_s(buf, 128, "Value: %f", constant_test<float>::DEG_TO_RAD);
    OutputDebugStringA(buf); // prints "Value: 1.#INF00"
    return 0;
}

An expression constant_test<float>::DEG_TO_RAD magically returns -Infinity

If i remove template parameter and make them only float then constant is evaluated correctly (0.017453)

If i add reference to HALF_CIRCLE constant then it's also evaluated correctly

I'm using MSVC 2013 SP 1.

Why? What i'm missing?

like image 617
acc15 Avatar asked Jan 01 '26 06:01

acc15


1 Answers

According to

  • c++ - Initialization order of static data inside class template - Stack Overflow
  • c++ - Template static members initialization order - Stack Overflow

there are the following types of initialization:

  • static initialization
    • zero initialization: for example int x; in global scope
    • constant initialization: for example int x=1+2; where 1+2 is a constant expression
  • dynamic initialization
    • ordered initialization: for example
      • std::vector<int> x{1, 2, 3}; in global scope, or
      • template<> std::vector<int> A<int>::x{1, 2, 3}; (explicit specialization)
    • unordered initialization: for example template<class T> std::vector<T> A<T>::x;

See What is Unordered dynamic initialization, Partially-ordered dynamic initialization and Ordered dynamic initialization for what the terms mean.

In this case

template <typename T> const T constant_test<T>::PI = 3.141592653589f;
template <typename T> const T constant_test<T>::FULL_CIRCLE = 360.0f;

are constant initialization(s), and because they're floating-point type in this case they're not constexpr, according to c++ - Initializing constexpr with const: Different treatment for int and double - Stack Overflow.

template <typename T> const T constant_test<T>::HALF_CIRCLE = constant_test<T>::FULL_CIRCLE / 2;
template <typename T> const T constant_test<T>::DEG_TO_RAD = constant_test<T>::PI / constant_test<T>::HALF_CIRCLE;

are dynamic unordered initialization.

As such, the initialization order of the 2 last ones are not guaranteed.

like image 159
user202729 Avatar answered Jan 03 '26 10:01

user202729