Порядок инициализации и уничтожения статической области видимости блока в сравнении с областью имен - PullRequest
0 голосов
/ 02 января 2019

Я пытаюсь понять правила секвенирования для инициализации и уничтожения объектов области имен и области действия со статической продолжительностью хранения и продолжительностью локального хранилища в контексте основного потока.Рассмотрим эти два класса:

struct Foo {
    Foo() { std::cout << "Foo\n"; }
    ~Foo() { std::cout << "~Foo\n"; }
    static Foo &instance();
};
struct Bar {
    Bar() { std::cout << "Bar\n"; }
    ~Bar() { std::cout << "~Bar\n"; }
    static Bar &instance();
};

Они идентичны, за исключением реализаций их статических функций-членов instance:

thread_local Foo t_foo;
Foo &Foo::instance() { return t_foo; }

Bar &Bar::instance() { static Bar s_bar; return s_bar; }

Bar - это синглтон Мейерса, блок-объект области действия со статической продолжительностью хранения.

Foo - это объект области имен пространства имен с продолжительностью локального хранения потока.

Теперь функция main:

int main() {
    Bar::instance();
    Foo::instance();
}

Вот вывод из GCC 8.1.0 и Clang 5.0.0:

Bar
Foo
~Foo
~Bar

Попробуйте его вживую: https://coliru.stacked -crooked.com / a / f83a9ec588aed921

Я ожидал, что сначала будет создан Foo, потому что он находится в области имен.Я предполагаю, что реализация может отложить инициализацию до первого использования объекта.Я не знал, что это может быть отложено до инициализации статической области блока, но я могу с этим смириться.

Теперь я изменил порядок вызовов функций в main:

int main() {
    Foo::instance();
    Bar::instance();
}

А вот вывод:

Foo
Bar
~Foo
~Bar

Теперь я перенес первое использование odr экземпляра Foo до первого вызова Bar::instance, и порядокинициализация, как я и ожидал.

Но я думал, что объекты должны быть уничтожены в обратном порядке их инициализации, которая, как представляется, не происходит.Чего мне не хватает?

В отношении инициализации и уничтожения объектов статической и локальной потоковой памяти, cppreference и стандарта говорят такие вещи, как «когда запускается программа», «когда запускается поток»,«когда заканчивается программа» и «когда заканчивается поток», но как эти понятия связаны друг с другом в контексте основного потока?Или, если быть более точным, первый поток и последний поток?

В моей "реальной" проблеме Foo (локальный поток) используется регистратором и деструктором базового классаBar использует логгер, так что это фиаско статического порядка уничтоженияДругие потоки создаются, но Bar (синглтон Мейерса) создается и уничтожается в основном потоке.Если бы я мог понять правила последовательности, то я мог бы попытаться решить «реальную» проблему, не прибегая к случайным попыткам.

1 Ответ

0 голосов
/ 02 января 2019

Стандарт гарантирует, что уничтожение Foo (локальное хранилище потока) будет раньше Bar (статическое хранилище):

[basic.start.term] / 2

Завершение деструкторов для всех инициализированных объектов с продолжительностью хранения потока в этом потоке сильно происходит до инициации деструкторов любого объекта со статической продолжительностью хранения.

Тем не менее, нет никаких гарантий о порядке строительства.Стандарт только говорит, что локальный поток должен быть создан перед его первым использованием odr:

[basic.stc.thread] / 2

Переменнаяс продолжительностью хранения потока инициализируется до его первого использования odr

...