内联变量模拟:我可以使用全局静态引用来强制执行函数中静态变量的初始化顺序吗?

inline variable emulation: can I use global static references to enforce the initialization order of static variable in functions?

N4424 引入了 inline 变量(可以在多个翻译单元中定义的变量,在 header-only 库中很有用),但它不在 C++14 中。我一直在用下面的方法来模拟它:

// a.hpp
#pragma once // to simplify
inline A& a_init() { static A a_obj; return a_obj; }
static A& a = a_init();

// b.hpp
#pragma once // to simplify
#include "a.hpp"
inline B& b_init() { static B b_obj{ use(a) }; return b_obj; }
static B& b = b_init();

cpp 文件使用 ab。没有其他使用 a_initb_init.

我认为:

  1. a must be initialized before b,在每个包含 b;
  2. 的翻译单元中
  3. 由于没有其他调用 b_initb_init 仅在初始化 b 时调用。从1那时候a肯定已经初始化了(a_init肯定已经返回);
  4. a_obj is initialized when a_init is calledb_obj 也是 b_init
  5. 结合2和3,a_obja在调用b_init之前初始化,因此use(a)是安全的,初始化顺序必须是A() < B()A() returns,则调用B());
  6. Destructors run in the reverse order of the completion of their construction, 所以 ~B() < ~A().

我的推理是否正确?这是 inline A a; inline B b{ use(a) }; 的工作仿真并保证 A() < B() < ~B() < ~A() 的顺序吗?

在不同翻译单元(即.cpp 文件)中定义的静态变量的初始化顺序可以唤起所谓的static initialization order fiasco

幸运的是,有一种方法可以使用 Construct On First Use Idiom 来防止这种惨败。你所做的很像那个成语。

现在关于你的情况:由于 header "b.hpp" 在 b 的定义之前包含 header "a.hpp"。当在翻译单元中包含 "b.hpp" 时,也会包含 "a.hpp"。预处理器将分别用 headers "a.hpp" 和 "b.hpp" 的代码替换 include 指令,并且因为 #include "a.hpp"b 的定义之上,所以 [=由于 "within the same compilation unit the order is well defined (i.e., the same order as definition)",13=] 将在 b 之前定义。因此,您强制使用正确的定义顺序。

因此,您的推理成立。我不知道这是否是 inline A a; inline B b{ use(a) };.

的有效模拟