10

In C++ 11, un distruttore senza alcuna specifica di eccezione viene implicitamente dichiarato con noexcept, che è una modifica da C++ 03. Pertanto, un codice che usava lanciare dai distruttori in C++ 03 sarebbe comunque ben compilato in C++ 11, ma si arresterebbe in fase di runtime quando tenterà di lanciare da un tale distruttore.Transizione a C++ 11 dove i distruttori sono implicitamente dichiarati con nient'altro

Poiché non vi è alcun errore in fase di compilazione con un tale codice, come potrebbe essere tranquillamente passato a C++ 11, a meno di dichiarare tutti i distruttori esistenti nella base di codice come noexcept(false), che sarebbe davvero eccessivo verboso e intrusivo, o ispezionare ogni distruttore per essere potenzialmente di lancio, il che sarebbe davvero dispendioso in termini di tempo e incline a fare, o catturare e correggere tutti gli arresti anomali in fase di runtime, cosa che non garantirebbe mai tutti questi casi?

+1

Interessante sembra che la revisione principale di gcc fornisca un avviso per questo tramite '-Wterminate'. Non vedo un'opzione simile per clang. –

+2

Come ho sempre detto, le specifiche delle eccezioni C++ sono uno scherzo. Scusa, non costruttivo asnwer, ma condivido il tuo dolore. – SergeyA

+0

@ShafikYaghmour Questo è interessante ma il mio GCC 5.2.0 non riconosce l'opzione. Hai un riferimento per questo? – 5gon12eder

risposta

2

Una volta ho passato lo stesso dilemma.

Fondamentalmente quello che ho concluso è che, accettando il fatto che quei distruttori stanno lanciando e semplicemente vivendo con le conseguenze di ciò è di solito molto peggio che passare attraverso il dolore di farli non buttare.

La ragione è che si rischiano stati ancora più volatili e imprevedibili quando si lanciano distruttori.

Ad esempio, ho lavorato a un progetto una volta in cui, per vari motivi, alcuni sviluppatori utilizzavano eccezioni per il controllo del flusso in alcune parti del progetto e funzionava perfettamente per anni. Più tardi, qualcuno ha notato che in una parte diversa del progetto, a volte il client non riusciva a inviare alcuni messaggi di rete che doveva inviare, così hanno creato un oggetto RAII che avrebbe inviato i messaggi nel suo distruttore. A volte il networking generava un'eccezione, quindi questo distruttore RAII avrebbe lanciato, ma chi se ne frega? Non ha memoria da ripulire, quindi non è una perdita.

E ciò funzionerebbe correttamente per il 99% delle volte, tranne quando il percorso di controllo del flusso delle eccezioni si verificava per attraversare il collegamento in rete, che genera anche un'eccezione. E poi, hai due eccezioni live che vengono svolte in una sola volta, quindi "bang sei morto", nelle parole immortali delle FAQ C++.

Onestamente preferirei che il programma si interrompesse all'istante quando un distruttore getta, quindi possiamo parlare con chi ha scritto il distruttore di lancio, piuttosto che provare a mantenere un programma con intenzionalmente a lanciare i distruttori, e questo è il consenso del comitato/comunità sembra. Così hanno fatto questo cambiamento decisivo per aiutarti ad affermare che i tuoi distruttori sono bravi e non lanciano. Potrebbe essere molto lavoro se il tuo codice base ha molti hack, ma se vuoi continuare a svilupparlo e mantenerlo, almeno sullo standard C++ 11, è meglio fare il lavoro di pulizia su i distruttori.

linea di fondo:

Hai ragione, non si può davvero sperare di garantire che a trovare tutte le possibili istanze di un distruttore di lancio. Quindi ci saranno probabilmente degli scenari in cui, quando il tuo codice viene compilato in C++ 11, si bloccherà nei casi in cui non sarebbe conforme allo standard C++ 98. Ma nel complesso, ripulire i distruttori e correre come C++ 11 sarà probabilmente molto più stabile che andare con i distruttori di lancio sui vecchi standard.

+0

Non c'è consenso sul fatto che lanciare i dtors sia una cattiva idea. Alcune persone ben note non fanno un consenso. – curiousguy

+0

Ok, forse non un consenso, ma certamente una maggioranza. Immagino di non poter parlare per tutti, ma quelli che non si oppongono al lancio di dvd sono così rari che non ho mai incontrato qualcuno che pensa che non dovrebbe essere contrario agli standard di codifica, e non ho mai visto una libreria o una struttura software che fa affidamento sui deambulatori. Ho visto solo un mucchio di codice di applicazione legacy che fa questo. –

+0

L'impressione di un consenso è sempre esagerata dal pensiero di gruppo e dall'intimidazione. Gli argomenti contro i drippers lanciatori sono molto deboli.La discussione si riduce a "buttare i dvd causa problemi" (che è anche vero per MI), "lanciare i fustori portare a termine" (che è anche vero per 'throw()'), e nel "Io non voglio pensa al problema ". Nessuno vuole affrontare il problema che i medici potrebbero lanciare perché non possono svolgere il proprio lavoro, proprio come qualsiasi altra funzione. È necessario affrontare la questione della gestione degli errori negli operatori. – curiousguy

6

Si noti che le regole non sono in realtà così brutali. Il distruttore sarà implicitamente solo noexcept se un distruttore implicitamente dichiarato sarebbe. Pertanto, contrassegnando almeno una classe di base o un tipo di membro come noexcept (false) si avvelenerà il valore noexcept di tutta la gerarchia/aggregato.

#include <type_traits> 

struct bad_guy 
{ 
    ~bad_guy() noexcept(false) { throw -1; } 
}; 

static_assert(!std::is_nothrow_destructible<bad_guy>::value, 
       "It was declared like that"); 

struct composing 
{ 
    bad_guy member; 
}; 

static_assert(!std::is_nothrow_destructible<composing>::value, 
       "The implicity declared d'tor is not noexcept if a member's" 
       " d'tor is not"); 

struct inheriting : bad_guy 
{ 
    ~inheriting() { } 
}; 

static_assert(!std::is_nothrow_destructible<inheriting>::value, 
       "The d'tor is not implicitly noexcept if an implicitly" 
       " declared d'tor wouldn't be. An implicitly declared d'tor" 
       " is not noexcept if a base d'tor is not."); 

struct problematic 
{ 
    ~problematic() { bad_guy {}; } 
}; 

static_assert(std::is_nothrow_destructible<problematic>::value, 
       "This is the (only) case you'll have to look for."); 

Tuttavia, sono d'accordo con Chris Beck che si dovrebbe sbarazzarsi dei tuoi distruttori lancio prima o poi. Possono anche far esplodere il programma C++ 98 nei momenti più scomodi.

+0

È stato molto utile, grazie! Dal momento che non c'era una risposta vera, però, ho finito per rispondere a me stesso; Mi chiedo se tu sia d'accordo con la mia linea di pensiero lì. – dragonroot

3

Come già accennato, 5gon12eder, esistono alcune regole che determinano un distruttore senza una specifica di eccezione da dichiarare implicitamente come noexcept o noexcept(false). Se il tuo distruttore può lanciare e lasciare che il compilatore decida la sua specifica di eccezione, giocherebbe una roulette, perché ti affidi alla decisione del compilatore influenzata dagli antenati e dai membri della classe, dai loro antenati e membri in modo ricorsivo , che è troppo complesso da tracciare ed è soggetto a modifiche durante l'evoluzione del tuo codice. Pertanto, quando si definisce un distruttore con un corpo che può essere lanciato e nessuna specifica di eccezione, deve essere dichiarato esplicitamente come noexcept(false). D'altra parte, se sei certo che il corpo non può lanciare, puoi dichiararlo più esplicito, noexcept e aiutare il compilatore a ottimizzare, ma fai attenzione se scegli di farlo, perché se un distruttore di un membro/l'antenato della tua classe decide di lanciare, il tuo codice si interromperà in fase di runtime.

Si noti che qualsiasi distruttore o distruttore implicitamente definito con corpi vuoti non pone problemi. Sono implicitamente solo noexcept se tutti i distruttori di tutti i membri e gli antenati sono anche noexcept.

Il modo migliore per procedere con la transizione è quindi trovare tutti i distruttori con corpi non vuoti e nessuna specifica di eccezione e dichiarare ognuno di essi che può lanciare con noexcept(false). Nota che devi solo controllare il corpo del distruttore - qualsiasi tiro immediato o qualsiasi tiro fatto dalle funzioni che chiama, in modo ricorsivo. Non è necessario controllare i distruttori con corpi vuoti, distruttori con una specifica di eccezione esistente o qualsiasi distruttore implicitamente definito. In pratica, non ci sarebbero molti di quelli che devono essere controllati, in quanto l'uso prevalente di questi è semplicemente la liberazione delle risorse.

Dato che sto rispondendo a me stesso, è esattamente quello che ho fatto nel mio caso e non è stato poi così doloroso.

+0

Penso che questo sia un buon approccio. Quando scrivo codice, mi piace sempre essere esplicito e dichiarare i distruttori come "noexcept" anche se non è strettamente necessario perché sarebbe comunque il default. Vorrei precisare che non stai "lasciando la decisione al compilatore" se non sei esplicito - queste regole sono scritte nello standard e il compilatore non ha margine di manovra per applicarle. Sicuro per i bug del compilatore, ovviamente. – 5gon12eder

+0

In realtà ho dovuto rivedere la parte in cui ho raccomandato di contrassegnare tutti i distruttori con i corpi senza lancio 'noxcept', poiché i loro membri e antenati possono ancora lanciare, e ciò comporterebbe l'interruzione se il distruttore è contrassegnato come' noxcept'. Per quanto riguarda le regole, sì, quelle sono molto esplicite - difficile da rintracciare da un umano. – dragonroot