函数调用的静态初始化顺序
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 正在初始化。前者阻止了常量初始化,后者消除了动态初始化的所有顺序约束。显然,在这种情况下,修复可能是微不足道的。
我有一个用函数调用初始化的静态全局变量。这会导致不遵守初始化顺序,甚至在翻译单元内部也是如此。这是一个复制器(代码的顺序在这里没有什么意义,但它类似于我用例中的原始排列,并且对触发问题很重要,所以请无视):
#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 正在初始化。前者阻止了常量初始化,后者消除了动态初始化的所有顺序约束。显然,在这种情况下,修复可能是微不足道的。