静态内联成员初始化顺序

Static inline members initialization order

C++ 中一个众所周知的问题是 static initialization order fiasco。当使用 C++17 static inline members 时,它仍然被认为是一个问题吗?

这是一个静态内联成员在两个不同的翻译单元(a.cpp 和 b.cpp)中用作两个非内联静态成员的初始值设定项的示例:

counter.hh

#pragma once

#include <vector>
#include <fstream>

class Counter
{
    public:
        Counter()  { std::ofstream os("o.txt", std::ofstream::app); os << "Counter created" << std::endl; }
        ~Counter() { std::ofstream os("o.txt", std::ofstream::app); os << "Counter destroyed" << std::endl; }
        void add_instance()    
        { 
            ++m_instances; 
            std::ofstream os("o.txt", std::ofstream::app); os << "Counter increased: " << m_instances << std::endl; 
        }
        void remove_instance() 
        { 
            --m_instances; 
            std::ofstream os("o.txt", std::ofstream::app); os << "Counter decreased: " << m_instances << std::endl; 
        }

    private:
        int m_instances = 0;
};

class Object
{
    public:
        Object(Counter & counter) : m_counter(counter) 
        {
            m_counter.add_instance(); 
            std::ofstream os("o.txt", std::ofstream::app); os << "Object created" << std::endl; 
        }
        ~Object() 
        { 
            m_counter.remove_instance(); 
            std::ofstream os("o.txt", std::ofstream::app); os << "Object destroyed" << std::endl; 
        }

    private:
        Counter & m_counter;
};

struct C
{
    static inline Counter static_counter{};
};

a.hh

#pragma once

#include "counter.hh"

struct A
{
    static Object static_a; //not inline
};

a.cpp

#include "a.hh"

Object A::static_a{C::static_counter};

b.hh

#pragma once

#include "counter.hh"

struct B
{
    static Object static_b; //not inline
};

b.cpp

#include "b.hh"

Object B::static_b{C::static_counter};

main.cpp

#include "a.hh"
#include "b.hh"

int main() { }

输出(使用 MSVC 16.1.2)

Counter created
Counter increased: 1
Object created
Counter increased: 2
Object created
Counter decreased: 1
Object destroyed
Counter decreased: 0
Object destroyed
Counter destroyed

我认为,关于初始化,这种做法是安全的,因为 C++17 标准确保静态内联成员:(1) 始终在任何使用前初始化,以及 (2) 跨多个只初始化一次翻译单位。

但我想知道这种模式是否有任何隐藏的缺点,例如与不同 TU 中每个变量的销毁顺序有关。是否明确定义 static_astatic_b 总是在 static_counter 之前销毁?

是的,这很好,因为在每个翻译单元中 static_counterdefined before static_a/static_b. Destruction order is not guaranteed to be the reverse (given threads, this is meaningless anyway), but the reverse of each guarantee holds,所以也可以。