函数调用的静态初始化顺序

Order of static initialization with function call

我有一个用函数调用初始化的静态全局变量。这会导致不遵守初始化顺序,甚至在翻译单元内部也是如此。这是一个复制器(代码的顺序在这里没有什么意义,但它类似于我用例中的原始排列,并且对触发问题很重要,所以请无视):

#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;}

这里我有一个 TestClass class ,其构造函数将 arr 成员变量初始化为其默认值,如 defaultValue<std::array<float,3>> 特化所定义;后者是一个全局变量,其值设置为等于 getDefaultArrayValue() 的 return 值。创建了 TestClass 的全局实例,我希望它的 arr 成员正确初始化为指定的默认值,即 3.40282e+38(即最大浮点值)。然而,运行 程序打印了值 0.,因为 defaultValue<std::array<float, 3>> 的初始化实际上是在 tc:

创建之后完成的
$ ./a.out 
Build TestClass
Initialize array
0

所以当 tc 构建时 defaultValue<std::array<float, 3>> 仍然没有正确的值。如果 defaultValue<std::array<float, 3>> 是通过直接为其赋值(请参阅复制器代码中的注释行)而不是调用函数来初始化的,则恢复适用的“从第一个到最后一个”静态变量初始化顺序规则到单个翻译单元。

总而言之,似乎调用函数来初始化全局变量会破坏初始化顺序。此行为是标准规定的还是实现细节(我使用的是 GCC 11.2.0)?

不是你使用了函数调用:而是那个函数不是 constexpr,它是一个(a 的特化)变量 template 正在初始化。前者阻止了常量初始化,后者消除了动态初始化的所有顺序约束。显然,在这种情况下,修复可能是微不足道的。