Especificaciones de excepciones (throw, noexcept) (C++)

Las especificaciones de excepciones son una característica del lenguaje C++ que indica la intención del programador sobre los tipos de excepciones que puede propagar una función. Puede especificar que una función podría salir o no mediante una excepción con una especificación de excepciones. El compilador puede usar esta información para optimizar las llamadas a la función y finalizar el programa si una excepción inesperada escapa de la función.

Antes de C++17 había dos tipos de especificación de excepciones. La especificación noexcept era una novedad de C++11. Especifica si el conjunto de posibles excepciones que pueden escapar de la función está vacío. La especificación dinámica de excepciones, o especificación throw(optional_type_list), quedó en desuso en C++11 y se quitó en C++17, excepto para throw(), que es un alias de noexcept(true). Esta especificación de excepciones se diseñó para proporcionar información de resumen sobre qué excepciones se pueden iniciar desde una función, pero en la práctica resultó ser problemática. La única especificación de excepciones dinámica que resultó ser útil de alguna manera fue la especificación throw() incondicional. Por ejemplo, la declaración de función:

void MyFunction(int i) throw();

indica al compilador que la función no produce ninguna excepción. Aun así, en el modo /std:c++14, esto podría provocar un comportamiento indefinido si la función produce una excepción. Por lo tanto, se recomienda usar el operador noexcept en lugar del anterior:

void MyFunction(int i) noexcept;

En la tabla siguiente se resume la implementación de Microsoft C++ de las especificaciones de excepciones:

Especificación de la excepción Significado
noexcept
noexcept(true)
throw()
La función no produce ninguna excepción. En el modo /std:c++14 (que es el valor predeterminado), noexcept y noexcept(true) son equivalentes. Cuando se produce una excepción desde una función declarada noexcept o noexcept(true), se invoca std::terminate. Cuando se produce una excepción desde una función declarada como throw() en el modo /std:c++14, el resultado es un comportamiento indefinido. No se invoca ninguna función específica. Se trata de una divergencia del estándar de C++14, que requería que el compilador invocara std::unexpected.
Visual Studio 2017, versión 15.5 y posteriores: en el modo /std:c++17, noexcept, noexcept(true) y throw() son equivalentes. En el modo /std:c++17, throw() es un alias para noexcept(true). En el modo /std:c++17 y versiones posteriores, cuando se produce una excepción desde una función declarada con cualquiera de estas especificaciones, se invoca std::terminate según lo requiera el estándar de C++17.
noexcept(false)
throw(...)
Sin especificación
La función puede producir una excepción de cualquier tipo.
throw(type) (C++14 y versiones anteriores) La función puede producir una excepción del tipo type. El compilador acepta la sintaxis, pero la interpreta como noexcept(false). En el modo /std:c++17 y versiones posteriores, el compilador emite la advertencia C5040.

Si se usa el control de excepciones en una aplicación, debe haber una función en la pila de llamadas que controle las excepciones producidas antes de salir del ámbito externo de una función marcada como noexcept, noexcept(true) o throw(). Si alguna función llamada entre la que produce una excepción y la que controla la excepción se especifica como noexcept, noexcept(true) (o throw() en el modo /std:c++17), el programa finaliza cuando la función noexcept propaga la excepción.

El comportamiento de excepción de una función depende de los factores siguientes:

Las especificaciones de excepciones explícitas no se permiten en las funciones de C. Se supone que una función de C no inicia excepciones en /EHsc y puede producir excepciones estructuradas en /EHs, /EHa o /EHac.

En la tabla siguiente se resume si una función de C++ podría iniciarse en varias opciones de control de excepciones del compilador:

Función /EHsc /EHs /EHa /EHac
Función de C++ sin especificación de excepciones
Función de C++ con la especificación de excepciones noexcept, noexcept(true) o throw() No No
Función de C++ con la especificación de excepciones noexcept(false), throw(...) o throw(type)

Ejemplo

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

Consulte también

Instrucciones try, throw y catch (C++)
Procedimientos recomendados de C++ moderno para las excepciones y el control de errores