Nifty/Schwarz计数器,符合标准?

And*_*ron 16 c++ static-order-fiasco

我今天早上和一位同事就静态变量初始化顺序进行了讨论.他提到了Nifty/Schwarz计数器,我(有点)感到困惑.我理解它是如何工作的,但我不确定这在技术上是否符合标准.

假设以下3个文件(前两个是来自更多C++习语的 copy-pasta ):


//Stream.hpp
class StreamInitializer;

class Stream {
   friend class StreamInitializer;
 public:
   Stream () {
   // Constructor must be called before use.
   }
};
static class StreamInitializer {
  public:
    StreamInitializer ();
    ~StreamInitializer ();
} initializer; //Note object here in the header.
Run Code Online (Sandbox Code Playgroud)
//Stream.cpp
static int nifty_counter = 0; 
// The counter is initialized at load-time i.e.,
// before any of the static objects are initialized.
StreamInitializer::StreamInitializer ()
{
  if (0 == nifty_counter++)
  {
    // Initialize Stream object's static members.
  }
}
StreamInitializer::~StreamInitializer ()
{
  if (0 == --nifty_counter)
  {
    // Clean-up.
  }
}
Run Code Online (Sandbox Code Playgroud)
// Program.cpp
#include "Stream.hpp" // initializer increments "nifty_counter" from 0 to 1.

// Rest of code...
int main ( int, char ** ) { ... }
Run Code Online (Sandbox Code Playgroud)

......这就是问题所在!有两个静态变量:

  1. "nifty_counter"in Stream.cpp; 和
  2. "初始化者" Program.cpp.

由于这两个变量恰好位于两个不同的编译单元中,因此在调用构造函数之前没有(AFAIK)官方保证nifty_counter初始化为0 initializer.

我可以想到两个快速的解决方案作为两个为什么这个"有效":

  1. 现代编译器足够聪明,可以解决两个变量之间的依赖关系,并将代码以适当的顺序放在可执行文件中(极不可能);
  2. nifty_counter 实际上是在"加载时"初始化,就像文章说的那样,它的值已经放在可执行文件的"数据段"中,因此它总是在"运行任何代码之前"初始化(极有可能).

在我看来,这些都依赖于一些非官方但可能的实现.这个标准是否合规,或者这只是"非常可行",我们不应该担心它?

Jer*_*fin 21

我相信它确保有效.根据标准($ 3.6.2/1):"具有静态存储持续时间(3.7.1)的对象应在进行任何其他初始化之前进行零初始化(8.5)."

由于nifty_counter具有静态存储持续时间,因此initializer无论跨翻译单元的分布如何,它都会在创建之前进行初始化.

编辑:重新阅读相关部分后,考虑来自@Tadeusz Kopec评论的输入,我不太确定它是否正确定义,但确定它是明确的定义非常简单的:删除从定义初始化nifty_counter,所以它看起来像:

static int nifty_counter;
Run Code Online (Sandbox Code Playgroud)

由于它具有静态存储持续时间,即使没有指定初始化程序也会进行零初始化 - 并且删除初始化程序会消除在零初始化之后发生的任何其他初始化的任何疑问.

  • @Tadeusz - 不,该标准还说(3.6.2继续)具有常量表达式作为初始化器的对象也在需要动态初始化的对象之前初始化. (11认同)
  • 这是零初始化.以后编译器是否会根据其初始化表达式尝试初始化`nifty_counter`,可能是在有人已经增加它之后? (2认同)