Namespaces
Variants

Dynamic exception specification (until C++17)

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous
Exceptions
try block
Throwing exceptions
Handling exceptions
Exception specification
noexcept specification (C++11)
dynamic specification ( until C++17* )
noexcept operator (C++11)

Enumera las excepciones que una función podría lanzar directa o indirectamente.

Contenidos

Sintaxis

throw( type-id-list  (opcional) ) (1) (obsoleto en C++11)
(eliminado en C++17)
1) Especificación explícita de excepción dinámica.
type-id-list - lista separada por comas de type-ids , un type-id que representa una pack expansion va seguido de puntos suspensivos (...) (desde C++11)

Una especificación de excepción dinámica explícita deberá aparecer únicamente en un declarador de función para un tipo de función, puntero a tipo de función, referencia a tipo de función, o puntero a tipo de función miembro que sea el tipo de nivel superior de una declaración o definición, o en dicho tipo que aparezca como parámetro o tipo de retorno en un declarador de función.

void f() throw(int);            // OK: declaración de función
void (*pf)() throw (int);       // OK: declaración de puntero a función
void g(void pfa() throw(int));  // OK: declaración de parámetro de puntero a función
typedef int (*pf)() throw(int); // Error: declaración typedef

Explicación

Si una función se declara con el tipo T listado en su especificación de excepciones dinámicas, la función puede lanzar excepciones de ese tipo o de un tipo derivado del mismo.

Tipos incompletos , punteros o referencias a tipos incompletos distintos de cv void* , y tipos de referencia a valor (desde C++11) no están permitidos en la especificación de excepción. Los tipos de array y función, si se utilizan, se ajustan a los tipos de puntero correspondientes, y las calificaciones cv de nivel superior también se eliminan. paquetes de parámetros están permitidos (desde C++11) .

Una especificación de excepción dinámica cuyo conjunto de tipos ajustados está vacío (después de expandir cualquier paquete) (desde C++11) es no-lanzadora. Una función con una especificación de excepción dinámica no-lanzadora no permite ninguna excepción.

Una especificación de excepción dinámica no se considera parte del tipo de una función.

Si la función lanza una excepción de un tipo no listado en su especificación de excepciones, se llama a la función std::unexpected . La función por defecto llama a std::terminate , pero puede ser reemplazada por una función proporcionada por el usuario (mediante std::set_unexpected ) que puede llamar a std::terminate o lanzar una excepción. Si la excepción lanzada desde std::unexpected es aceptada por la especificación de excepciones, el desenrollado de pila continúa normalmente. Si no lo es, pero std::bad_exception está permitido por la especificación de excepciones, se lanza std::bad_exception . De lo contrario, se llama a std::terminate .

Instanciación

La especificación de excepción dinámica de una especialización de plantilla de función no se instancia junto con la declaración de la función; se instancia solo cuando es necesaria (como se define a continuación).

La especificación de excepción dinámica de una función miembro especial declarada implícitamente también se evalúa solo cuando es necesario (en particular, la declaración implícita de una función miembro de una clase derivada no requiere que la especificación de excepción de una función miembro base sea instanciada).

Cuando la especificación de excepción dinámica de una especialización de plantilla de función es necesaria , pero aún no se ha instanciado, los nombres dependientes se buscan y cualquier plantilla utilizada en la expression se instancia como si fuera para la declaración de la especialización.

Una especificación de excepción dinámica de una función se considera necesaria en los siguientes contextos:

  • en una expresión, donde la función es seleccionada por resolución de sobrecarga
  • la función es odr-used
  • la función sería odr-used pero aparece en un operando no evaluado
template<class T>
T f() throw(std::array<char, sizeof(T)>);
int main()
{
    decltype(f<void>()) *p; // f no evaluada, pero se necesita la especificación de excepción
                            // error porque la instanciación de la especificación de excepción
                            // calcula sizeof(void)
}
  • la especificación es necesaria para comparar con otra declaración de función (por ejemplo, en un reemplazador de función virtual o en una especialización explícita de una plantilla de función)
  • en una definición de función
  • la especificación es necesaria porque una función miembro especial predeterminada necesita verificarla para decidir su propia especificación de excepciones (esto ocurre solo cuando la especificación de la función miembro especial predeterminada es, en sí misma, necesaria).

Excepciones potenciales

Cada función f , puntero a función pf , y puntero a función miembro pmf tiene un conjunto de excepciones potenciales , que consiste en tipos que podrían ser lanzados. El conjunto de todos los tipos indica que cualquier excepción puede ser lanzada. Este conjunto se define de la siguiente manera:

1) Si la declaración de f , pf , o pmf utiliza una especificación de excepción dinámica que no permite todas las excepciones (hasta C++11) , el conjunto consiste en los tipos listados en esa especificación.
2) En caso contrario, si la declaración de f , pf , o pmf utiliza noexcept(true) , el conjunto está vacío.
(desde C++11)
3) De lo contrario, el conjunto es el conjunto de todos los tipos.

Nota: para las funciones miembro especiales declaradas implícitamente (constructores, operadores de asignación y destructores) y para los constructores heredados (since C++11) , el conjunto de excepciones potenciales es una combinación de los conjuntos de excepciones potenciales de todo lo que llamarían: constructores/operadores de asignación/destructores de miembros de datos no estáticos no variantes, bases directas y, cuando corresponda, bases virtuales (incluyendo expresiones de argumentos predeterminados, como siempre).

Cada expresión e tiene un conjunto de excepciones potenciales . El conjunto está vacío si e es una expresión constante central , de lo contrario, es la unión de los conjuntos de excepciones potenciales de todas las subexpresiones inmediatas de e (incluyendo expresiones de argumentos predeterminados ), combinado con otro conjunto que depende de la forma de e , como sigue:

1) Si e es una expresión de llamada a función, sea g la función, puntero a función, o puntero a función miembro que es invocado, entonces
  • si la declaración de g utiliza una especificación de excepción dinámica, el conjunto de excepciones potenciales de g se añade al conjunto;
(desde C++11)
  • de lo contrario, el conjunto es el conjunto de todos los tipos.
2) Si e llama a una función implícitamente (es una expresión de operador y el operador está sobrecargado, es una new-expression y la función de asignación está sobrecargada, o es una expresión completa y se llama al destructor de un temporal), entonces el conjunto es el conjunto de esa función.
3) Si e es una throw-expression , el conjunto es la excepción que sería inicializada por su operando, o el conjunto de todos los tipos para la throw-expression de relanzamiento (sin operando).
4) Si e es un dynamic_cast a una referencia de un tipo polimórfico, el conjunto consiste en std::bad_cast .
5) Si e es un typeid aplicado a un puntero desreferenciado a un tipo polimórfico, el conjunto consiste en std::bad_typeid .
6) Si e es una expresión new con un tamaño de arreglo no constante, y la función de asignación seleccionada tiene un conjunto no vacío de excepciones potenciales, el conjunto consiste en std::bad_array_new_length .
(desde C++11)
void f() throw(int); // el conjunto de f() es "int"
void g();            // el conjunto de g() es el conjunto de todos los tipos
struct A { A(); };                  // el conjunto de "new A" es el conjunto de todos los tipos
struct B { B() noexcept; };         // el conjunto de "B()" está vacío
struct D() { D() throw (double); }; // el conjunto de new D es el conjunto de todos los tipos

Todas las funciones miembro declaradas implícitamente y los constructores heredados (since C++11) tienen especificaciones de excepciones, seleccionadas de la siguiente manera:

  • Si el conjunto de excepciones potenciales es el conjunto de todos los tipos, la especificación de excepciones implícita permite todas las excepciones (la especificación de excepciones se considera presente, aunque es inexpresable en código y se comporta como si no hubiera especificación de excepciones) (hasta C++11) es noexcept ( false ) (desde C++11) .
  • En caso contrario, si el conjunto de excepciones potenciales no está vacío, la especificación de excepciones implícita enumera cada tipo del conjunto.
  • En caso contrario, la especificación de excepciones implícita es throw ( ) (hasta C++11) noexcept ( true ) (desde C++11) .
struct A
{
    A(int = (A(5), 0)) noexcept;
    A(const A&) throw();
    A(A&&) throw();
    ~A() throw(X);
};
struct B
{
    B() throw();
    B(const B&) = default; // la especificación de excepción es "noexcept(true)"
    B(B&&, int = (throw Y(), 0)) noexcept;
    ~B() throw(Y);
};
int n = 7;
struct D : public A, public B
{
    // Puede lanzar una excepción de un tipo que coincidiría con un manejador de tipo
    // std​::​bad_array_new_length, pero no lanza una excepción de mala asignación
    (void*) new (std::nothrow) int[n];
    // D puede tener los siguientes miembros declarados implícitamente:
    // D::D() throw(X, std::bad_array_new_length);
    // D::D(const D&) noexcept(true);
    // D::D(D&&) throw(Y);
    // D::~D() throw(X, Y);
};

Notas

Clang considera que la regla de instanciación de la especificación de excepciones dinámicas cambió en C++11 según CWG1330 , consulte LLVM #56349 .

Palabras clave

throw

Ejemplo

Nota: es mejor compilar en modo C++98 para evitar advertencias. Incompatible con C++17 y revisiones más recientes.

#include <cstdlib>
#include <exception>
#include <iostream>
class X {};
class Y {};
class Z : public X {};
class W {};
void f() throw(X, Y) 
{
    bool n = false;
    if (n)
        throw X(); // OK, would call std::terminate()
    if (n)
        throw Z(); // also OK
    throw W(); // will call std::unexpected()
}
void handler()
{
    std::cerr << "That was unexpected!\n"; // flush needed
    std::abort();
}
int main()
{
    std::set_unexpected(handler);
    f();
}

Salida:

That was unexpected!

Informes de defectos

Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.

DR Aplicado a Comportamiento publicado Comportamiento correcto
CWG 25 C++98 el comportamiento de asignación e inicialización
entre punteros a miembros con diferentes
especificaciones de excepción no estaba especificado
aplicar la restricción
para punteros a funciones
y referencias
CWG 973 C++98 la especificación de excepción puede contener tipos de función, pero la
conversión correspondiente de puntero a función no estaba especificada
especificada
CWG 1330 C++98 una especificación de excepción podría ser instanciada inmediatamente solo se instancia si es necesario
CWG 1267 C++11 se permitían tipos de referencia a valor derecho en especificaciones de excepción no permitido
CWG 1351 C++98
C++11
el argumento predeterminado (C++98) y el inicializador de miembro predeterminado
(C++11) se ignoraban en la especificación de excepción implícita
se hace que sean considerados
CWG 1777 C++11 throw ( T... ) no era una especificación no-lanzadora
incluso si T es un paquete vacío
es no-lanzadora
si el paquete está vacío
CWG 2191 C++98 el conjunto de excepciones potenciales de una expresión typeid
podría contener bad_typeid incluso si no puede ser lanzada
contiene bad_typeid
solo si puede ser lanzada

Véase también

noexcept especificador (C++11) especifica si una función puede lanzar excepciones