Порядок инициализации статических встроенных элементов - PullRequest
3 голосов
/ 09 июня 2019

Хорошо известной проблемой в C ++ является статический порядок инициализации фиаско .По-прежнему ли это считается проблемой, когда одно использование C ++ 17 статических встроенных элементов ?

Вот пример, где статический встроенный элемент используется в двух разных единицах перевода (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_a, и static_b всегда уничтожаются до static_counter?

1 Ответ

1 голос
/ 10 июня 2019

Да, это нормально, поскольку в каждой единице перевода static_counter равен , определенному ранее static_a / static_b.Порядок уничтожения не гарантированно будет обратным (учитывая потоки, в любом случае это бессмысленно), но обратный каждой гарантии содержит , так что это тоже работает.

...