La norma non copre questo caso; la lettura più restrittiva sarebbe che è legale inizializzare un thread_local
nel distruttore di un oggetto con durata di archiviazione statica, ma è illegale consentire al programma di continuare con il normale completamento.
Il problema si pone in [basic.start.term]:
1 - distruttori ([class.dtor]) per gli oggetti inizializzati (cioè oggetti la cui vita ([basic.life]) è iniziato) con durata di conservazione statica sono chiamati come risultato del ritorno da main e come risultato della chiamata a std :: exit ([support.start.term]). I distruttori per oggetti inizializzati con durata di memorizzazione del thread all'interno di un dato thread sono chiamati come risultato del ritorno dalla funzione iniziale di quel thread e come risultato di quel thread che chiama std :: exit. I completamenti dei distruttori per tutti gli oggetti inizializzati con durata di memorizzazione dei thread all'interno di quel thread sono sequenziati prima dell'avvio dei distruttori di qualsiasi oggetto con durata di archiviazione statica. [...]
Così il completamento del bar::~Bar::foo::~Foo
viene sequenziato prima dell'inizio della bar::~Bar
, che è una contraddizione.
L'unica get-out potrebbe essere quella di sostenere che [basic.start.term]/1 si applica solo agli oggetti la cui vita ha iniziato al punto di terminazione del programma/thread, ma contra[stmt.dcl] ha:
5 - Il distruttore per un oggetto con ambito di blocco con durata di memorizzazione statica o di thread verrà eseguito se e solo se è stato creato. [Nota: [basic.start.term] descrive l'ordine in cui gli oggetti a livello di blocco con durata di archiviazione statica e di thread vengono distrutti. - nota end]
Questo è chiaramente destinato ad essere applicato esclusivamente alla discussione normale e la terminazione del programma, dal ritorno dalla principale o da una funzione filo, o chiamando std::exit
.
Inoltre, [basic.stc.thread] ha:
Una variabile con durata di conservazione filo è inizializzato prima del primo uso ODR-([basic.def.odr]) e, se costruita, sono distrutti all'uscita filo .
Il "deve" è un'istruzione per l'implementatore, non per l'utente.
Si noti che non vi è nulla di sbagliato nell'iniziare la vita del distruttore con ambito thread_local
, poiché [basic.start.term]/2 non si applica (non è stato precedentemente distrutto). Questo è il motivo per cui credo che un comportamento indefinito si verifichi quando si consente al programma di continuare con il normale completamento.
Domande simili sono state poste prima, anche se si tratta di una durata di archiviazione statica o statica anziché di thread_local
rispetto a statica; Destruction of objects with static storage duration (e https://groups.google.com/forum/#!topic/comp.std.c++/Tunyu2IJ6w0) e Destructor of a static object constructed within the destructor of another static object. Sono propenso a concordare con James Kanze su quest'ultima domanda che [defns.undefined] si applica qui e il comportamento non è definito perché lo standard non lo definisce. Il miglior modo per avanzare sarebbe per qualcuno in piedi per aprire un rapporto sui difetti (che copre tutte le combinazioni di static
se thread_local
s inizializzate nei distruttori di static
s e thread_local
s), per sperare in una risposta definitiva.
legale o meno, perché lo faresti? –
@DavidHaim Sto tentando di implementare parte di 'libC++ abi' (' __cxa_thread_atexit() 'in particolare), ed ero curioso di sapere se dovessi gestire questo caso o meno. –
Probabilmente è solo "cout" che viene distrutto prima di "foo". Prova a lanciare un'eccezione dal distruttore di 'Foo' e vedi se viene chiamato' std :: terminate'. –