I have a static global variable initialized with a function call. This causes the initialization order to be not respected, eve inside a translation unit. This is a reproducer (the order of the code makes little sense here but it resembles the original arrangement in my use case, and is important to trigger the issue, so please disregard it):
#include <array>
#include <iostream>
#include <limits>
template <typename T> constexpr T defaultValue = std::numeric_limits<T>::max();
namespace _Private {
template <typename T> std::array<T, 3> getDefaultArrayValue() {
std::cout << "Initialize array" << std::endl;
std::array<T, 3> result;
result[0] = defaultValue<T>;
result[1] = defaultValue<T>;
result[2] = defaultValue<T>;
return result;
}
} // namespace _Private
// This generates the wrong behavior
template <typename T> const std::array<T, 3> defaultValue<std::array<T, 3>> = _Private::getDefaultArrayValue<T>();
// This behaves right
//template <typename T> const std::array<T, 3> defaultValue<std::array<T, 3>> = {defaultValue<T>, defaultValue<T>, defaultValue<T>};
struct TestClass {
std::array<float, 3> arr;
TestClass();
};
TestClass tc;
int main() {
std::cout << tc.arr[0] << std::endl;
return 0;
}
TestClass::TestClass() : arr{defaultValue<std::array<float, 3>>} {std::cout << "Build TestClass" << std::endl;}
Here I have a TestClass
class whose constructor initializes the arr
member variable to its default value, as defined by the defaultValue<std::array<float,3>>
specialization; the latter is a global variable whose value is set equal to the return value of getDefaultArrayValue()
. A global instance of TestClass
is created, and I would expect that its arr
member is correctly initialized to the specified default, that is 3.40282e+38 (i.e. the maximum float value). However, running the program the value 0. is printed, because the initialization of defaultValue<std::array<float, 3>>
is actually done after the creation of tc
:
$ ./a.out
Build TestClass
Initialize array
0
so when tc
is built defaultValue<std::array<float, 3>>
still does not have its correct value. The problem is fixed if defaultValue<std::array<float, 3>>
is initialized by directly assigning a value to it (see the commented line in the reproducer code) rather than calling a function, restoring the "from first to last" static variable initialization order rule that applies to a single translation unit.
To summarize, it seems that calling a function to initialize a global variable breaks the initialization order. Is this behavior mandated by the standard or is it an implementation detail (I'm using GCC 11.2.0)?