初始化与类类型相同的静态成员(静态初始化顺序问题)

Initialization of static member with same type as class (static initialization order problem)

本文关键字:初始化 静态 顺序 问题 静态成员 类型      更新时间:2023-10-16

>假设我有一个类Super,其中我有一个类型为Super的静态成员,它只是定义了Super的常用实例

// super.hpp
class Super
{
public:
Super(const double a):
m_a(a)
{}
double a() const { return m_a; }
static const Super thing; // use this a lot in code
private:
double m_a;
};

与实施

// super.cpp
#include "super.hpp"
const Super Super::thing = Super(1.0); // definition

在这一点上,我认为一切都很好,但如果不是,请纠正我。

现在我也有Super的子类,具有类似的静态成员

// sub.hpp
#include "super.hpp"
class Sub : public Super
{
public:
Sub(const double a):
Super(a)
{}
explicit Sub(const Super& obj):
Super(obj)
{}
static const Sub thing;
};

与实施

// sub.hpp
#include "sub.hpp"
const Sub Sub::thing = Sub(Super::thing); // WORKS IN MYSTERIOUS WAYS

最后是一个用法示例

// main.cpp
#include <iostream>
#include "sub.hpp"
int main()
{
Super super = Super::thing;
std::cout << super.a() << std::endl;
Sub sub = Sub::thing;
std::cout << sub.a() << std::endl;
}

当我使用以下设置编译它时

// CMakeLists.txt
project (hello)
add_executable(hello main.cpp super.cpp sup.cpp)

我得到了预期的输出

$ ./hello 
1
1

但是如果我在CMakeLists中更改 cpp 文件的顺序(sub.cppsuper.cpp之前

(
// CMakeLists.txt
project (hello)
add_executable(hello main.cpp sup.cpp super.cpp)

我得到

$ ./hello 
1
0

我认为这是静态初始化顺序"惨败"(?(的一个例子,我对它发生的原因有一些了解。

所以问题变成了:有没有办法获得有关未初始化静态的警告,或者有任何方法可以避免这个问题?

我已经阅读了如何防止"静态初始化顺序问题"?,但我希望能够保留Sub::thing接口,并避免将其替换为Sub::thing()

根据您的评论,我做了以下工作:

  • 使构造函数constexpr
  • static成员constexpr inline(您必须将它们放入头文件中,而不是.cpp(,因此需要 C++17

现在,您的静态变量已constexpr,因此它们将被静态初始化,因此静态初始化顺序的惨败不会对它们发生。

所以,这个解决方案对我有用:

class Super
{
public:
constexpr Super(const double a):
m_a(a)
{}
double a() const { return m_a; }
static const Super thing;
private:
double m_a;
};
constexpr inline Super Super::thing = Super(1.0);
class Sub : public Super
{
public:
constexpr Sub(const double a):
Super(a)
{}
constexpr explicit Sub(const Super& obj):
Super(obj)
{}
static const Sub thing;
};
constexpr inline Sub Sub::thing = Sub(Super::thing);
#include <iostream>
int main()
{
Super super = Super::thing;
std::cout << super.a() << std::endl;
Sub sub = Sub::thing;
std::cout << sub.a() << std::endl;
}

有没有办法得到关于未初始化的静态的警告,

我不知道

或者有什么方法可以避免这个问题?

  • 您可以避免全局成员,并使用延迟初始化将全局包装在函数中,因此更改:

    // header
    class Super {
    static const Super thing;
    // ...
    };
    //  cpp file
    const Super Super::thing = Super(1.0); // definition
    

    // header
    class Super {
    static const Super& thing();
    // ...
    };
    //  cpp file
    const Super& Super::thing() { static const Super instance{1.0}; return instance; }
    

    和类似

    class Sub : public Super
    {
    public:
    // ...
    static const Sub thing;
    };
    // sub.cpp
    const Sub Sub::thing = Sub(Super::thing); 
    

    class Sub : public Super
    {
    public:
    // ...
    static const Sub& thing();
    };
    // sub.cpp
    const Sub& Sub::thing() { static const Sub instance(Super::thing()); return instance; }
    
  • 或者将所有全局放在一个翻译单元中,因为顺序是有保证的。

    // global.cpp
    #include "super.hpp"
    #include "sub.hpp"
    const Super Super::thing = Super(1.0);    // Defined in order
    const Sub Sub::thing = Sub(Super::thing); // so you have control