15

Эта программа:Является ли законным инициализировать переменную thread_local в деструкторе глобальной переменной?

#include <iostream> 

struct Foo { 
    Foo() { 
     std::cout << "Foo()\n"; 
    } 

    ~Foo() { 
     std::cout << "~Foo()\n"; 
    } 
}; 

struct Bar { 
    Bar() { 
     std::cout << "Bar()\n"; 
    } 

    ~Bar() { 
     std::cout << "~Bar()\n"; 
     thread_local Foo foo; 
    } 
}; 

Bar bar; 

int main() { 
    return 0; 
} 

Печать

Bar() 
~Bar() 
Foo() 

для меня (GCC 6.1, Linux, x86-64). ~ Foo() никогда не вызывается. Это ожидаемое поведение?

+0

юридический или нет, зачем вам это делать? –

+3

@DavidHaim Я пытаюсь реализовать часть 'libC++ abi' (' __cxa_thread_atexit() 'в частности), и мне было любопытно, должен ли я обрабатывать этот случай или нет. –

+5

Это, вероятно, просто 'cout' уничтожается до' foo'. Попробуйте выбросить исключение из деструктора 'Foo' и посмотреть, вызывается ли' std :: terminate'. –

ответ

9

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

Проблема возникает в [basic.start.term]:

1 - деструкторы ([class.dtor]) для инициализированных объектов (то есть, те объекты, срок службы ([basic.life]) началось) со статической продолжительностью хранения вызываются в результате возврата из main и в результате вызова std :: exit ([support.start.term]). Деструкторы для инициализированных объектов с длительностью хранения потока в заданном потоке вызываются в результате возврата из исходной функции этого потока и в результате этого потока, вызывающего std :: exit. По завершении деструкторов для всех инициализированных объектов с длительностью хранения потоков в этом потоке секвенируются перед началом деструктора любого объекта со статической продолжительностью хранения. [...]

Так завершение bar::~Bar::foo::~Foo секвенировали до начала bar::~Bar, что является противоречием.

только получить выход может быть утверждать, что [basic.start.term]/1 применяется только к объектам, время жизни которых началось в момент окончания программы/нить, но против[stmt.dcl] имеет:

5 - Деструктор для объекта с объемной областью со статическими или потоковыми хранилищами будет выполняться тогда и только тогда, когда он был создан. [Примечание: [basic.start.term] описывает порядок, в котором уничтожаются объекты области области с статикой и продолжительностью хранения потоков. - конец примечание]

Это явно предназначено для применения только к нормальной нити и завершению программы, по возвращению из основных или из функции потока, или позвонив по телефону std::exit.

Кроме того, есть [basic.stc.thread]:

Переменная с длительностью хранения нити должны быть инициализированы перед первым ODR использования ([basic.def.odr]), и, если построено, должно быть уничтожено на выходе резьбы ,

Здесь «инструкция» должна быть инструкцией для разработчика, а не для пользователя.

Обратите внимание, что нет ничего плохого в том, чтобы начать с жизни thread_local, так как [basic.start.term]/2 не применяется (он ранее не был уничтожен). Вот почему я считаю, что неопределенное поведение происходит, когда вы разрешаете программе продолжать нормальное завершение.

Аналогичные вопросы задавали раньше, хотя о статической и статической длительности хранения, а не thread_local против статики; Destruction of objects with static storage durationhttps://groups.google.com/forum/#!topic/comp.std.c++/Tunyu2IJ6w0), и Destructor of a static object constructed within the destructor of another static object. Я склонен согласиться с Джеймсом Канзе на последнем вопросе, что здесь применяется [defns.undefined], и поведение не определено, поскольку стандарт не определяет его. Лучшим способом продвижения было бы для кого-то, кто стоял, чтобы открыть отчет о дефекте (охватывающий все комбинации static s и thread_local s, инициализированные в деструкторах static и thread_local с), чтобы надеяться на окончательный ответ.

+0

Это проясняет это для меня, спасибо! –

Смежные вопросы