Specyfikacje wyjątków (throw, noexcept) (C++)

Specyfikacje wyjątków to funkcja języka C++, która wskazuje intencję programisty na temat typów wyjątków, które mogą być propagowane przez funkcję. Można określić, że funkcja może lub nie może zostać zakończona przez wyjątek, używając specyfikacji wyjątku. Kompilator może użyć tych informacji, aby zoptymalizować wywołania funkcji i przerwać działanie programu, jeśli nieoczekiwany wyjątek spowoduje ucieczkę funkcji.

Przed C++17 istniały dwa rodzaje specyfikacji wyjątków. Specyfikacja noexcept była nowa w języku C++11. Określa, czy zestaw potencjalnych wyjątków, które mogą uciec od funkcji, jest pusty. Specyfikacja wyjątku dynamicznego lub throw(optional_type_list) specyfikacja została uznana za przestarzałą w języku C++11 i usunięta w języku C++17, z wyjątkiem elementu throw(), który jest aliasem dla noexcept(true)języka . Ta specyfikacja wyjątku została zaprojektowana w celu udostępnienia podsumowania informacji o tym, jakie wyjątki mogą zostać wyrzucone z funkcji, ale w praktyce okazało się, że jest to problematyczne. Jedną z dynamicznych specyfikacji wyjątków, która okazała się nieco przydatna, była bezwarunkowa throw() specyfikacja. Na przykład deklaracja funkcji:

void MyFunction(int i) throw();

informuje kompilator, że funkcja nie zgłasza żadnych wyjątków. Jednak w /std:c++14 trybie może to prowadzić do niezdefiniowanego zachowania, jeśli funkcja zgłasza wyjątek. Dlatego zalecamy użycie noexcept operatora zamiast powyższego:

void MyFunction(int i) noexcept;

Poniższa tabela zawiera podsumowanie implementacji specyfikacji wyjątków w języku Microsoft C++:

Specyfikacja wyjątku Znaczenie
noexcept
noexcept(true)
throw()
Funkcja nie zgłasza wyjątku. W /std:c++14 trybie (domyślnym) noexcept i noexcept(true) są równoważne. Gdy jest zgłaszany wyjątek z funkcji zadeklarowanej noexcept lub noexcept(true), std::terminate jest wywoływana. Gdy wyjątek jest zgłaszany z funkcji zadeklarowanej jako throw() w /std:c++14 trybie, wynik jest niezdefiniowany. Nie jest wywoływana żadna określona funkcja. Jest to rozbieżność ze standardu C++14, który wymagał od kompilatora wywołania metody std::unexpected.
Program Visual Studio 2017 w wersji 15.5 lub nowszej: w /std:c++17 trybie , noexcept, noexcept(true)i throw() są równoważne. W /std:c++17 trybie throw() jest aliasem dla elementu noexcept(true). W /std:c++17 trybie i nowszym, gdy wyjątek jest zgłaszany z funkcji zadeklarowanej przy użyciu dowolnej z tych specyfikacji, std::terminate jest wywoływany zgodnie z wymaganiami standardu C++17.
noexcept(false)
throw(...)
Brak specyfikacji
Funkcja może zgłosić wyjątek dowolnego typu.
throw(type) (C++14 i starsze) Funkcja może zgłosić wyjątek typu type. Kompilator akceptuje składnię, ale interpretuje ją jako noexcept(false). W /std:c++17 trybie i nowszym kompilator wyświetla ostrzeżenie C5040.

Jeśli obsługa wyjątków jest używana w aplikacji, musi istnieć funkcja w stosie wywołań, która obsługuje zgłoszone wyjątki przed wyjściem z zewnętrznego zakresu funkcji oznaczonej jako noexcept, noexcept(true)lub throw(). Jeśli jakiekolwiek funkcje wywoływane między tym, który zgłasza wyjątek i ten, który obsługuje wyjątek, są określone jako noexcept, noexcept(true) (lub throw() w /std:c++17 trybie), program zostanie zakończony, gdy funkcja noexcept propaguje wyjątek.

Zachowanie wyjątku funkcji zależy od następujących czynników:

  • Który standardowy tryb kompilacji języka jest ustawiony.

  • Niezależnie od tego, czy kompilujesz funkcję w języku C, czy C++.

  • Która /EH opcja kompilatora jest używana.

  • Czy jawnie określisz specyfikację wyjątku.

Jawne specyfikacje wyjątków nie są dozwolone w funkcjach języka C. Zakłada się, że funkcja języka C nie zgłasza wyjątków w obszarze /EHsc, i może zgłaszać wyjątki ustrukturyzowane w obszarze /EHs, /EHalub /EHac.

W poniższej tabeli podsumowano, czy funkcja języka C++ może potencjalnie zgłaszać różne opcje obsługi wyjątków kompilatora:

Function /EHsc /EHs /EHa /EHac
Funkcja C++ bez specyfikacji wyjątku Tak Tak Tak Tak
Funkcja języka C++ ze specyfikacją noexceptwyjątku , noexcept(true)lub throw() Nie. Nie. Tak Tak
Funkcja języka C++ ze specyfikacją noexcept(false)wyjątku , throw(...)lub throw(type) Tak Tak Tak Tak

Przykład

// 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

Zobacz też

try, throwi catch — Instrukcje (C++)
Nowoczesne najlepsze rozwiązania dotyczące języka C++ dotyczące wyjątków i obsługi błędów