问题描述
标准对程序退出期间函数局部静态初始化有什么看法?
编辑:为了清楚起见,我的意思是在代码示例中的情况 - 本地静态 b
是在构造另一个静态 a
之后构造的(因此,应该先销毁 b
a
),但是 b
也是在 a
的析构函数中构造的,所以它应该立即销毁吗?后? UB?
我没有找到任何关于此事的参考。
我想知道这种情况是否属于 UB,或者它是否应该具有某种定义的行为?
struct B{};
void foo()
{
static B b;
}
struct A
{
~A() { foo(); }
};
int main()
{
static A a;
return 0;
}
如你所见,A的析构函数会在程序退出时发生(因为它有静态存储),它会尝试构造一个B静态实例。
我对 C++17 更感兴趣,如果它在这个主题上有什么不同的话。
解决方法
鉴于 foo()
仅从 A
的析构函数中调用,因此我看不出这里有冲突。
来自[stmt.dcl]/5 [强调我的]:
具有静态或线程存储持续时间的块范围对象将被销毁当且仅当它被构造时。 [注意:[basic.start.term]描述了块范围的顺序具有静态和线程存储持续时间的对象被销毁。 — 尾注 ]
而且,来自[basic.start.term]/4 [重点我的]:
如果函数包含静态或线程存储持续时间的块范围对象,该对象已被销毁,并且在销毁具有静态或线程存储持续时间的对象期间调用该函数 /strong>,如果控制流通过先前销毁的块范围对象的定义,则程序具有未定义的行为。同样,如果块范围对象在其销毁后被间接使用(即通过指针),则行为未定义。
由于 b
中的 foo()
(在这个特定示例中)直到 a
中的 main()
被销毁时才会被构造,[basic.start.term]/4 将不得违反,因为此时 b
尚未销毁。
如果在销毁 foo()
中的 a
之前调用 main()
,情况就不同了。
由于对象 B 是由 A 的析构函数创建的,所以它会在程序退出之前被销毁。我在您的代码中添加了打印语句,使其看起来像这样:
#include <iostream>
struct B
{
~B()
{
std::cout << "Called B's Destructor...!";
}
};
void foo()
{
static B b;
}
struct A
{
~A() {
foo();
std::cout << "Called A's Destructor...!\n";
}
};
int main()
{
static A a;
return 0;
}
它打印以下内容:
Called A's Destructor...!
Called B's Destructor...!
底线是,静态变量将在程序结束时(本地或全局)销毁,并按照创建的顺序进行。