提问人:Audrius Meškauskas 提问时间:9/19/2023 最后编辑:Audrius Meškauskas 更新时间:9/23/2023 访问量:92
为什么 gcc 接受将方法中的静态变量初始化为对象的字段?
Why gcc accepts initializing the static variable within the method to the field of the object?
问:
class MyClass {
public:
int obj_field;
MyClass(int val) : obj_field(val) {}
void myMethod() {
static int my_static_var = obj_field; // <--- Why is this okay?
// ...
}
};
我深信这是错误的,不应该编译,因为我确信它们只能初始化为 constexpr(假设值在执行开始之前初始化)。令我惊讶的是,它在 gcc 9.4.0、C++17 中没有编译任何错误。它是否能够将静态变量的初始化推迟到第一次调用或未定义的行为更有可能发生?
我知道如果它能起作用,静态值可能会从调用该方法的第一个对象中选取。
答:
它是否能够将静态变量的初始化推迟到第一次调用
这实际上是初始化局部静态变量的点。更具体地说,初始化发生在执行首次到达声明点时。
还是更有可能出现未定义的行为?
示例中没有 UB。
为什么 gcc 接受将方法中的静态变量初始化为对象的字段?
因为这样做是很好的形式。
评论
(假设值在执行开始之前初始化)
这个假设(可能)有两点是错误的。
首先,在函数中初始化变量的时间与在命名空间范围内初始化变量的时间不同。虽然全局静力学(大部分)在执行之前被初始化(我猜这就是你所想到的),但局部静力学在执行其定义之前不会被初始化。这意味着直到(至少)第一次调用函数时才会初始化它。static
static
main
其次,执行不是从主
函数开始的。全局变量的初始化分为两个阶段。第一阶段处理您知道的常量表达式初始化。第二阶段是那些需要执行代码进行构造的变量。第二阶段可以在功能开始之前完成。(有一些回旋余地,允许第二阶段在开始 .)static
main
main
事实上,在初始化全局变量时执行代码的能力是静态顺序惨败的关键因素。更好的是,解决一些静态顺序惨败的一种方法是通过将变量定义包装在 getter 函数中来控制初始化顺序。这可确保在第一次调用函数时初始化变量,这必然是在第一次使用变量值之前。所以你可以说,在你的问题中,你已经解决了一个你不知道存在的问题。:)static
static
对于那些喜欢实验的人来说,这里有一个简单的例子,说明命名空间范围内的变量可以使用非常量表达式进行初始化。static
#include <chrono>
#include <iostream>
static const auto start_time =
std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
int main() {
std::cout << "Program started at: " << std::ctime(&start_time) << "\n";
}
直接回答这个问题:
是的,初始化被推迟到第一次调用 ,并且值将来自该调用的对象。my_static_var
myMethod()
*this
评论
void myMethod(int x) { static int my_static_var = x; }