【问题标题】:Function-local static initialization during program exit程序退出期间的函数局部静态初始化
【发布时间】:2021-09-06 08:56:44
【问题描述】:

标准对程序退出期间的函数局部静态初始化有什么规定? 编辑:为清楚起见,我的意思是代码示例中的一个案例 - 本地静态 b 在构造另一个静态 a 之后构造(因此应该在 a 之前销毁 b),但是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 在这个主题上有什么不同的话,我对它更感兴趣。

【问题讨论】:

  • 程序已经为静态变量保留了空间,因为它们基本上是只能在特定范围内访问的全局变量。 AFAIK 这段代码 100% 没问题,应该做你期望它做的事情。
  • 从这里的答案来看,行为在 C++11 之前是未指定的,并且从那以后保证以相反的顺序进行破坏。 stackoverflow.com/questions/3143180/…
  • 本段 "...如果线程本地或静态对象 A 的构造函数或动态初始化的完成顺序在线程本地或静态对象 B 之前,则完成B 的破坏是有序的——在 A 的破坏开始之前..." 但我们已经在 A 的析构函数中,所以我们自相矛盾 - en.cppreference.com/w/cpp/utility/program/exit
  • 标准参考basic.start.term 与上述问题相同

标签: c++ static language-lawyer object-lifetime


【解决方案1】:

我没有看到这里有冲突,因为 foo() 只是从 A 的析构函数中调用的。

来自[stmt.dcl]/5 [强调我的]:

具有静态或线程存储持续时间的块作用域对象将被销毁当且仅当它被构造时。 [注意:[basic.start.term] 描述了块作用域的顺序具有静态和线程存储持续时间的对象被销毁。 ——尾注]

并且,来自[basic.start.term]/4 [强调我的]:

如果一个函数包含一个静态或线程存储持续时间的块范围对象已被销毁,并且该函数在静态对象的销毁过程中被调用或线程存储持续时间,如果控制流通过先前销毁的块作用域对象的定义,则程序具有未定义的行为。同样,如果块作用域对象在销毁后被间接使用(即通过指针),则行为未定义。

由于foo() 中的b (在此特定示例中)直到main() 中的a 被销毁才构造,因此不会违反[basic.start.term]/4,因为b 是此时尚未销毁。

如果在main() 中销毁a 之前调用foo(),则情况不同。

【讨论】:

  • 我不是说有冲突。实际上,只是没有任何信息。除了[basic.start.term]/4 之外,该标准没有说明析构函数内部的构造,这不适用于这种情况,因为之前没有调用foo()。我认为这是一个缺陷,应该在标准中提到这一点。
【解决方案2】:

由于对象 B 是由 A 的析构函数创建的,所以它会在程序退出之前被销毁。我在您的代码中添加了 print 语句,使其看起来像这样:

#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...!

最重要的是,静态变量将在程序结束时(本地或全局)销毁,并按照创建的顺序。

【讨论】:

  • 一个很好的答案,但在这种情况下,请注意问题上的 [language-lawyer] 标签以及标签对答案的要求。
  • 抱歉!我错过了。我应该删除这个答案吗?
  • 谢谢!我知道它编译并成功运行,但它也可能发生在 UB 的情况下......我正在尝试获得更正式的答案
  • 我不认为这是对所发布问题的回答,该问题询问标准说了什么以及这是否是未定义的行为
猜你喜欢
  • 1970-01-01
  • 2011-02-26
  • 2013-07-21
  • 1970-01-01
  • 1970-01-01
  • 2013-09-22
  • 2023-01-30
  • 2020-04-30
  • 1970-01-01
相关资源
最近更新 更多