Я пытаюсь понять правила секвенирования для инициализации и уничтожения объектов области имен и области действия со статической продолжительностью хранения и продолжительностью локального хранения потока в контексте основного потока. Рассмотрим эти два класса:
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
(синглтон Мейерса) построен и разрушен в главном потоке. Если бы я мог понять правила секвенирования, то я мог бы попытаться решить «реальную» проблему, не прибегая к случайным попыткам.
Стандарт гарантирует, что уничтожение Foo
(поток локального хранилища) до Bar
(статическое хранилище):
Завершение деструкторов для всех инициализированных объектов с продолжительностью хранения потока в этом потоке сильно происходит до инициации деструкторов любого объекта со статической продолжительностью хранения.
Тем не менее, нет никаких гарантий о порядке строительства. Стандарт только говорит, что локальный поток должен быть создан перед его первым использованием odr:
Переменная с продолжительностью хранения потока должна быть инициализирована перед первым использованием odr.
Других решений пока нет …