Порядок инициализации и уничтожения статической области видимости блока или области видимости thread_local в основном потоке

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

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

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

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

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

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

6

Решение

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

[Basic.start.term] / 2

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

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

[Basic.stc.thread] / 2

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

5

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]