尽管初始化,模板类的静态成员 std::function 仍为空

mil*_*aki 6 c++ lambda templates std std-function

考虑下面的类:

template <class T>
struct Test
{
    Test() {
        if (!f) {
            f = []() { std::cout << "it works\n"; };
            initialized = true;
        }
    }
    static void check() {
        if (f) f();
        else std::cout << "f is empty\n";
    }
    T x{};
    inline static std::function<void()> f;
    inline static bool initialized = false;
};
Run Code Online (Sandbox Code Playgroud)

此类的对象,如果全局声明(是的,我知道使用全局变量是一种不好的做法f),则在初始化后似乎会清空该函数。以下示例演示了这一点:

Test<double> t;

int main()
{
    t.x = 1.5;
    std::cout << "t.x = " << t.x << "\n";
    std::cout << std::boolalpha << "initalized: " <<  Test<double>::initialized << "\n";
    Test<double>::check();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这打印:

t.x = 1.5
initalized: true
f is empty
Run Code Online (Sandbox Code Playgroud)

我期望Test<double>::check();打印it works.

但是,当我执行以下任一操作时,上面的示例会按预期工作:

  • t内声明main()
  • 不要使用模板Test(只需替换Tdouble例如)
  • 使fcheck()静态
  • 使用普通函数指针代替std::function

有谁知道为什么会发生这种情况?

Jac*_*ack 8

该问题与静态变量的初始化顺序有关,我猜想与Test<double>不同编译器相比,模板化实例化静态变量的解决方式有所不同。

inline static Holder f;
Run Code Online (Sandbox Code Playgroud)

是一个静态变量,因此在进入 main 之前的某个地方它将被默认初始化(到一个空函数)。但Test<double>是另一个静态变量,它将在进入 main 之前获得自己的初始化。

在 GCC 上,发生的情况是

  • Test<double>叫做
  • Test<double>::f由构造函数设置Test<double>
  • 的默认构造函数Test<double>::f被调用,从而清空该函数

这一切都发生在__static_initialization_and_destruction_0GCC 方法内部,如果您实际上使用包装对象来中断变量的静态初始化,您可以看到发生了什么: https: //onlinegdb.com/UYEJ0hbgg

编译器如何知道您计划在构造之前从另一个静态变量设置一个静态变量?这就是为什么,正如您所说,使用静态变量确实是一种不好的做法。