Спецификации исключений (throw, noexcept) (C++)

Спецификации исключений — это функция языка C++, указывающая намерение программиста о типах исключений, которые могут распространяться функцией. Можно указать, что функция может или не выйти из исключения с помощью спецификации исключения. Компилятор может использовать эти сведения для оптимизации вызовов функции и завершения программы, если непредвиденное исключение выходит из функции.

До C++17 существовали два типа спецификации исключений. Спецификация noexcept была новой в C++11. Он указывает, является ли набор потенциальных исключений, которые могут экранировать функцию, пуста. Спецификация динамического исключения или throw(optional_type_list) спецификация устарела в C++11 и удалена в C++17, за исключением throw()псевдонима noexcept(true). Эта спецификация исключений была разработана для предоставления сводных сведений о том, какие исключения могут быть выброшены из функции, но на практике было обнаружено, что это проблематично. Одна спецификация динамического исключения, которая оказалась несколько полезной, была безусловной throw() спецификацией. Например, объявление функции:

void MyFunction(int i) throw();

сообщает компилятору, что функция не создает исключений. Однако в /std:c++14 режиме это может привести к неопределенному поведению, если функция создает исключение. Поэтому рекомендуется использовать noexcept оператор вместо приведенного выше:

void MyFunction(int i) noexcept;

В следующей таблице приведены сведения о реализации спецификаций исключений Microsoft C++:

Спецификация исключений Значение
noexcept
noexcept(true)
throw()
Функция не вызывает исключений. В /std:c++14 режиме (по умолчанию) noexcept и noexcept(true) эквивалентны. При возникновении исключения из функции, объявленной noexcept или noexcept(true)std::terminate вызываемой. Если исключение создается из функции, объявленной как throw() в /std:c++14 режиме, результат не определен. Не вызывается определенная функция. Это расхождение со стандартом C++14, которое требуется компилятору для вызова std::unexpected.
Visual Studio 2017 версии 15.5 и более поздних версий: в /std:c++17 режиме , noexceptnoexcept(true)и throw() все эквивалентны. В /std:c++17 режиме throw() является псевдонимом для noexcept(true). В /std:c++17 режиме и более поздних версиях, когда исключение создается из функции, объявленной с любой из этих спецификаций, std::terminate вызывается в соответствии со стандартом C++17.
noexcept(false)
throw(...)
Нет спецификации
Функция может вызывать исключение любого типа.
throw(type) (C++14 и более ранних версий) Функция может вызывать исключение типа type. Компилятор принимает синтаксис, но интерпретирует его как noexcept(false). В /std:c++17 режиме и более поздних версиях компилятор выдает предупреждение C5040.

Если обработка исключений используется в приложении, в стеке вызовов должна быть функция, которая обрабатывает исключения, прежде чем они выходят из внешней область функции, помеченной noexceptили noexcept(true)throw(). Если какие-либо функции, вызываемые между тем, который вызывает исключение, и тот, который обрабатывает исключение, указывается как noexceptnoexcept(true) (или throw() в /std:c++17 режиме), программа завершается, когда функция noexcept распространяет исключение.

Поведение исключения функции зависит от следующих факторов:

  • Какой языковой стандартный режим компиляции задан.

  • В какой среде выполняется компиляция функции — в C или C++.

  • Какой /EH параметр компилятора вы используете.

  • Задана ли явно спецификация исключений.

Явные спецификации исключений не разрешено использовать для функций C. Предполагается, что функция C не создает исключения под , и может вызывать структурированные /EHscисключения в /EHs, /EHaили /EHac.

В следующей таблице приводится сводка о том, может ли функция C++ потенциально вызывать различные параметры обработки исключений компилятора:

Функция /EHsc /EHs /EHa /EHac
Функция C++ без спецификации исключений Да Да Да Да
Функция C++ с noexceptспецификацией исключений noexcept(true), или throw() спецификацией исключений No No Да Да
Функция C++ с noexcept(false)спецификацией исключений throw(...), или throw(type) спецификацией исключений Да Да Да Да

Пример

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

См. также

try, throwи catch операторы (C++)
Современные рекомендации по C++ по исключению и обработке ошибок