Ответ 1
Стандарт не охватывает этот случай; самым строгим показанием было бы то, что законно инициализировать thread_local
в деструкторе объекта со статической продолжительностью хранения, но запретить программе продолжать нормальное завершение.
Проблема возникает в [basic.start.term]:
1 - Деструкторы ([class.dtor]) для инициализированных объектов (т.е. объекты, чье время жизни ([basic.life]) началось) со статической продолжительностью хранения вызываются в результате возврата из основного и в результате вызова std:: exit ([support.start.term]). Деструкторы для инициализированных объектов с длительностью хранения потока в заданном потоке вызываются в результате возврата из исходной функции этого потока и в результате этого потока, вызывающего std:: exit. По завершении деструкторов для всех инициализированных объектов с длительностью хранения потоков в этом потоке секвенируются перед началом деструктора любого объекта со статической продолжительностью хранения. [...]
Итак, завершение bar::~Bar::foo::~Foo
секвенировано до начала bar::~Bar
, что является противоречием.
Единственный выход может состоять в том, чтобы утверждать, что [basic.start.term]/1 применяется только к объектам, срок жизни которых начался в момент завершения программы/потока, но contra [stmt.dcl] имеет:
5 - Деструктор для объекта области блока со статикой или длительностью хранения потока будет выполняться тогда и только тогда, когда он был создан. [Примечание: [basic.start.term] описывает порядок, в котором уничтожаются объекты области области с статикой и продолжительностью хранения потоков. - конечная нота]
Это явно предназначено для применения только к нормальному потоку и завершению программы, возврату из основного или из функции потока или вызову std::exit
.
Кроме того, [basic.stc.thread] имеет:
Переменная с длительностью хранения потока должна быть инициализирована до ее первого использования odr ([basic.def.odr]) и, если она построена, должна быть уничтожена при выходе потока.
"Здесь" - это инструкция для разработчика, а не для пользователя.
Обратите внимание, что нет ничего плохого в начале срока жизни thread_local
с помощью деструктора, так как [basic.start.term]/2 не применяется (он ранее не был уничтожен). Вот почему я считаю, что поведение undefined происходит, когда вы разрешаете программе продолжить нормальное завершение.
Аналогичные вопросы задавались раньше, хотя и о статической и статической длительности хранения, а не о thread_local
vs. static; Уничтожение объектов со статической продолжительностью хранения (и https://groups.google.com/forum/#!topic/comp.std.c++/Tunyu2IJ6w0) и Деструктор статического объекта, построенного в деструкторе другого статического объекта. Я склонен согласиться с Джеймсом Канзе по последнему вопросу о том, что [defns.undefined] применяется здесь, а поведение undefined, потому что Стандарт не определяет его. Лучшим способом продвижения для кого-то будет стоять, чтобы открыть отчет о дефекте (охватывающий все комбинации static
и thread_local
, инициализированные в деструкторах static
и thread_local
s), чтобы надеяться на окончательный ответ.