- Ci sono molti esempi di funzioni che conosco non verranno mai lanciate, ma per le quali il compilatore non può determinarlo da solo. Devo aggiungere noexcept alla dichiarazione di funzione in tutti questi casi?
noexcept
è complicato, poiché fa parte dell'interfaccia delle funzioni. In particolare, se si sta scrivendo una libreria, il codice client può dipendere dalla noexcept
proprietà. Può essere difficile modificarlo in un secondo momento, poiché potresti rompere il codice esistente. Potrebbe essere meno preoccupante quando si implementa il codice utilizzato solo dalla propria applicazione.
Se hai una funzione che non può essere lanciata, chiediti se rimarrà tale noexcept
o limiterebbe le future implementazioni? Ad esempio, potresti voler introdurre il controllo degli errori di argomenti illegali lanciando eccezioni (ad esempio, per i test unitari), oppure potresti dipendere da un altro codice di libreria che potrebbe modificare le sue specifiche di eccezione. In tal caso, è più sicuro essere conservatori e omettere noexcept
.
D'altra parte, se si è certi che la funzione non debba mai essere lanciata ed è corretto che faccia parte delle specifiche, è necessario dichiararla noexcept
. Tuttavia, tieni presente che il compilatore non sarà in grado di rilevare violazioni noexcept
se l'implementazione cambia.
- Per quali situazioni dovrei essere più attento all'uso di noexcept e per quali situazioni posso cavarmela con il noexcept implicito (falso)?
Esistono quattro classi di funzioni su cui dovresti concentrarti perché probabilmente avranno il maggiore impatto:
- operazioni di spostamento (sposta operatore di assegnazione e sposta costruttori)
- operazioni di scambio
- deallocatori di memoria (eliminazione operatore, eliminazione operatore [])
- distruttori (anche se questi sono implicitamente a
noexcept(true)
meno che tu non li crei noexcept(false)
)
Queste funzioni dovrebbero essere generalmente noexcept
ed è molto probabile che le implementazioni della libreria possano utilizzare la noexcept
proprietà. Ad esempio, è std::vector
possibile utilizzare operazioni di movimento senza lancio senza sacrificare forti garanzie di eccezione. Altrimenti, dovrà ricorrere agli elementi di copia (come in C ++ 98).
Questo tipo di ottimizzazione è a livello algoritmico e non si basa su ottimizzazioni del compilatore. Può avere un impatto significativo, soprattutto se gli elementi sono costosi da copiare.
- Quando posso realisticamente aspettarmi di osservare un miglioramento delle prestazioni dopo aver usato noexcept? In particolare, fornisci un esempio di codice per il quale un compilatore C ++ è in grado di generare un codice macchina migliore dopo l'aggiunta di noexcept.
Il vantaggio di noexcept
non specificare alcuna eccezione o throw()
è che lo standard consente ai compilatori una maggiore libertà quando si tratta di impilare lo svolgimento. Anche nel throw()
caso, il compilatore deve svolgere completamente lo stack (e deve farlo nell'esatto ordine inverso delle costruzioni di oggetti).
Nel noexcept
caso, d'altra parte, non è necessario farlo. Non è richiesto che lo stack debba essere svolto (ma al compilatore è ancora consentito farlo). Questa libertà consente un'ulteriore ottimizzazione del codice in quanto riduce il sovraccarico di essere sempre in grado di svolgere lo stack.
La domanda correlata su noexcept, svolgimento della pila e prestazioni va in maggiori dettagli sull'overhead quando è richiesto lo svolgimento della pila.
Raccomando anche il libro di Scott Meyers "Effective Modern C ++", "Articolo 14: Dichiarare le funzioni tranne se non emetteranno eccezioni" per ulteriori letture.
move_if_nothrow
(o whatchamacallit) vedrà un miglioramento delle prestazioni se c'è un mosse di movimento senza eccezioni.