Differenza tra C ++ 03 specificatore throw () C ++ 11 noexcept


100

C'è qualche differenza tra throw()e noexceptdiverso dall'essere controllati rispettivamente in fase di esecuzione e in fase di compilazione?

Questo articolo di Wikipedia C ++ 11 suggerisce che gli specificatori di lancio di C ++ 03 sono deprecati.
Perché così, è in noexceptgrado di coprire tutto ciò in fase di compilazione?

[Nota: ho controllato questa domanda e questo articolo , ma non sono riuscito a determinare il motivo valido per il ritiro.]


7
Accodring a questo bel articolo anche noexceptpuò incorrere in controlli runtime. La principale differenza tra loro è che la rottura noexceptcausa std::terminatementre la rottura throwcausa std::unexpected. Anche un comportamento di svolgimento della pila leggermente diverso in questi casi.
Fiktik

Non c'è niente di "tempo di compilazione" controllato con alcune specifiche di eccezione che è "runtime" controllato su altri. È solo un mito creato dagli oppositori delle specifiche di eccezione del C ++.
Curioso

Risposte:


129

Gli specificatori di eccezione sono stati deprecati perché gli specificatori di eccezione sono generalmente un'idea pessima . noexceptè stato aggiunto perché è l'unico uso ragionevolmente utile di uno specificatore di eccezione: sapere quando una funzione non genera un'eccezione. Così diventa una scelta binaria: funzioni che lanceranno e funzioni che non lanceranno.

noexceptè stato aggiunto invece di rimuovere solo tutti gli specificatori di lancio diversi dal throw()perché noexceptè più potente. noexceptpuò avere un parametro che in fase di compilazione si risolve in un booleano. Se il valore booleano è vero, allora si noexceptattacca. Se il valore booleano è falso, noexceptnon si attacca e la funzione potrebbe generare.

Quindi, puoi fare qualcosa del genere:

struct<typename T>
{
  void CreateOtherClass() { T t{}; }
};

Fa CreateOtherClasseccezione buttare? Potrebbe, se Til costruttore di default può farlo. Come lo diciamo? Come questo:

struct<typename T>
{
  void CreateOtherClass() noexcept(is_nothrow_default_constructible<T>::value) { T t{}; }
};

Quindi, CreateOtherClass()lancerà iff il costruttore predefinito del tipo dato lancia. Ciò risolve uno dei principali problemi con gli specificatori di eccezioni: la loro incapacità di propagare lo stack di chiamate.

Non puoi farlo con throw().


+1 Risposta utile, per me comunque. Sto ancora cercando una risposta che dica perché vorrei utilizzare noexcept. Non ho mai usato lo throw()specificatore, mai e sto tentando di determinare se noexcepteffettivamente fornisce qualche vantaggio (oltre alla documentazione controllata dal compilatore).
hmjd

Appena trovato questo stackoverflow.com/questions/10787766/... ...
hmjd

1
@NicolBolas d'accordo. ma se noexcept fosse una garanzia, il compilatore potrebbe verificare se una funzione può lanciare o meno in un distruttore. Potendo così avvertire un programmatore che una funzione non è eccetto o no.
Alex

2
@NicolBolas le chiamate di runtime std::terminate. che è MOLTO PEGGIORE ! il codice può intrufolarsi in versioni che hanno funzioni contrassegnate noexcept e vengono rilevate violazioni in fase di esecuzione (ovvero nei siti dei clienti). Volevo dire che il compilatore garantisce di generare codice che non genera eccezioni in primo luogo.
Alex

2
@NicolBolas: un'altra differenza degna di nota. Se una funzione è contrassegnata, throws()se viene generata un'eccezione, lo stack deve essere svolto fino all'ambito di quella funzione (quindi tutte le variabili automatiche nella funzione vengono distrutte) a quel punto terminate()viene chiamato (tramite unexpected()). Se una funzione è contrassegnata, noexceptse viene generata un'eccezione, viene chiamata terminate (lo svolgimento dello stack è un dettaglio definito dall'implementazione).
Martin York

33

noexcept non viene controllato in fase di compilazione.

Un'implementazione non deve rifiutare un'espressione semplicemente perché quando viene eseguita genera o potrebbe generare un'eccezione che la funzione contenitore non consente.

Quando una funzione dichiarata noexcepto throw()tenta di generare un'eccezione, l'unica differenza è che si chiama terminatee le altre chiamate unexpectede quest'ultimo stile di gestione delle eccezioni è stato effettivamente deprecato.


Ma se una funzione virtuale ha throw()/ noexcept, il controllo del tempo di compilazione assicura che lo abbia anche un overrider.
curioso

2

std::unexpected() viene chiamato dal runtime C ++ quando viene violata una specifica di eccezione dinamica: viene generata un'eccezione da una funzione la cui specifica di eccezione proibisce eccezioni di questo tipo.

std::unexpected() può anche essere chiamato direttamente dal programma.

In entrambi i casi, std::unexpectedchiama il file attualmente installato std::unexpected_handler. Le std::unexpected_handlerchiamate predefinite std::terminate.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.