Specifiche di eccezione (throw, noexcept) (C++)

Le specifiche di eccezione sono una funzionalità del linguaggio C++ che indica la finalità del programmatore sui tipi di eccezione che possono essere propagati da una funzione. È possibile specificare che una funzione può o meno uscire da un'eccezione usando una specifica di eccezione. Il compilatore può usare queste informazioni per ottimizzare le chiamate alla funzione e per terminare il programma se un'eccezione imprevista esegue l'escape della funzione.

Prima di C++17 c'erano due tipi di specifica di eccezione. La specifica noexcept è stata nuova in C++11. Specifica se il set di potenziali eccezioni che possono eseguire l'escape della funzione è vuoto. La specifica di eccezione dinamica, o throw(optional_type_list) specifica, è stata deprecata in C++11 e rimossa in C++17, ad eccezione throw()di , che è un alias per noexcept(true). Questa specifica di eccezione è stata progettata per fornire informazioni di riepilogo sulle eccezioni che possono essere generate da una funzione, ma in pratica si è verificato un problema. L'unica specifica di eccezione dinamica che ha dimostrato di essere un po' utile era la specifica incondizionato throw() . Ad esempio, la dichiarazione di funzione:

void MyFunction(int i) throw();

indica al compilatore che la funzione non genera alcuna eccezione. Tuttavia, in /std:c++14 modalità questo potrebbe causare un comportamento non definito se la funzione genera un'eccezione. È pertanto consigliabile usare l'operatore noexcept anziché quello precedente:

void MyFunction(int i) noexcept;

La tabella seguente riepiloga l'implementazione di Microsoft C++ delle specifiche di eccezione:

Specifica di eccezione Significato
noexcept
noexcept(true)
throw()
La funzione non genera eccezioni. In /std:c++14 modalità (che è l'impostazione predefinita) noexcept e noexcept(true) sono equivalenti. Quando viene generata un'eccezione da una funzione dichiarata noexcept o noexcept(true), std::terminate viene richiamata. Quando viene generata un'eccezione da una funzione dichiarata come throw() in /std:c++14 modalità, il risultato non è definito. Non viene richiamata alcuna funzione specifica. Si tratta di una divergenza rispetto allo standard C++14, che ha richiesto al compilatore di richiamare std::unexpected.
Visual Studio 2017 versione 15.5 e successive: in /std:c++17 modalità , noexcept, noexcept(true)e throw() sono tutti equivalenti. In /std:c++17 modalità è throw() un alias per noexcept(true). In /std:c++17 modalità e versioni successive, quando viene generata un'eccezione da una funzione dichiarata con una di queste specifiche, std::terminate viene richiamata come richiesto dallo standard C++17.
noexcept(false)
throw(...)
Nessuna specifica
La funzione può generare un'eccezione di qualsiasi tipo.
throw(type) (C++14 e versioni precedenti) La funzione può generare un'eccezione di tipo type. Il compilatore accetta la sintassi, ma lo interpreta come noexcept(false). In /std:c++17 modalità e versioni successive il compilatore genera l'avviso C5040.

Se la gestione delle eccezioni viene usata in un'applicazione, deve essere presente una funzione nello stack di chiamate che gestisce le eccezioni generate prima di uscire dall'ambito esterno di una funzione contrassegnata come noexcept, noexcept(true)o throw(). Se qualsiasi funzione chiamata tra quella che genera un'eccezione e quella che gestisce l'eccezione vengono specificate come noexcept, noexcept(true) (o throw() in /std:c++17 modalità), il programma viene terminato quando la funzione noexcept propaga l'eccezione.

Il comportamento di eccezione di una funzione dipende dai fattori seguenti:

  • Modalità di compilazione standard del linguaggio impostata.

  • Se la funzione viene compilata in C o C++.

  • Opzione /EH del compilatore usata.

  • Se la specifica di eccezione viene specificata in modo esplicito.

Le specifiche di eccezioni esplicite nelle funzioni C non sono consentite. Si presuppone che una funzione C non generi eccezioni in /EHsce possa generare eccezioni strutturate in /EHs, /EHao /EHac.

La tabella seguente riepiloga se una funzione C++ può potenzialmente generare in varie opzioni di gestione delle eccezioni del compilatore:

Funzione /EHsc /EHs /EHa /EHac
Funzione C++ senza alcuna specifica di eccezione
Funzione C++ con noexcept, noexcept(true)o throw() specifica di eccezione No No
Funzione C++ con noexcept(false), throw(...)o throw(type) specifica di eccezione

Esempio

// exception_specification.cpp
// compile with: /EHs
#include <stdio.h>

void handler() {
   printf_s("in handler\n");
}

void f1(void) throw(int) {
   printf_s("About to throw 1\n");
   if (1)
      throw 1;
}

void f5(void) throw() {
   try {
      f1();
   }
   catch(...) {
      handler();
    }
}

// invalid, doesn't handle the int exception thrown from f1()
// void f3(void) throw() {
//   f1();
// }

void __declspec(nothrow) f2(void) {
   try {
      f1();
   }
   catch(int) {
      handler();
    }
}

// only valid if compiled without /EHc
// /EHc means assume extern "C" functions don't throw exceptions
extern "C" void f4(void);
void f4(void) {
   f1();
}

int main() {
   f2();

   try {
      f4();
   }
   catch(...) {
      printf_s("Caught exception from f4\n");
   }
   f5();
}
About to throw 1
in handler
About to throw 1
Caught exception from f4
About to throw 1
in handler

Vedi anche

tryIstruzioni , throwe catch (C++)
Procedure consigliate C++ moderne per le eccezioni e la gestione degli errori