Espacios de nombres
Variantes
Acciones

Especificación de excepción dinámica (hasta C++17)

De cppreference.com
< cpp‎ | language

Lista las excepciones que una función puede lanzar directa o indirectamente.

Contenido

[editar] Sintaxis

throw(lista-id-tipos (opcional)) (1) (en desuso en C++11)
(eliminado en C++17)
1) Especificación de excepción dinámica explícita.
lista-id-tipos - lista separada por comas de identificadores-de-tipo, un id-de-tipo que representa una expansión de paquete va seguido de puntos suspensivos (...) (desde C++11)

Una especificación de excepción dinámica explícita aparecrá solamente 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 del tipo de nivel superior de una declaración o definición, o en dicho tipo que aparece como parámetro o tipo de retorno en un declarador de función.

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

[editar] 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 un tipo derivado de él.

Los tipos incompletos, punteros o referencias a tipos incompletos, aparte de void* calificado-cv, y tipos de referencia rvalue (desde C++11) no se permiten en la especificación de excepciones. Los tipos arrays y de función, si se usan, se ajustan a los tipos de puntero correspondientes, las calificaciones cv de alto nivel también se eliminan. Se permiten los paquetes de parámetros (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. Dicha función llama a std::terminate por defecto, pero puede reemplazarse con una función suministrada por el usuario (usando std::set_unexpected), que puede llamar a std::terminate o lanzar una excepción. Si la excepción lanzada desde std::unexpected se acepta por la especificación de excepciones, el desenredo de pila continúa de manera habitual. Si no es así, pero la especificación de excepciones permite std::bad_exception, se lanza std::bad_exception. De otra forma, se llama a std::terminate.


[editar] 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 los tipos que podrían lanzarse. Un conjunto de todos los tipos indica que cualquier excepción puede lanzarse. 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 la excepciones (hasta C++11), el conjunto consiste en los tipos listados en esa especificación.
2) En otro caso, si la declaración de f, pf, o pmf utiliza noexcept(true), el conjunto está vacío.
(desde C++11)
3) De otra manera, el conjunto es el conjunto de todos los tipos.

Nota: para las funciones miembro declaradas implícitamente (constructores, operadores de asignación, y destructores) y para los constructores heredables (desde C++11), el conjunto de excepciones potenciales es una combinación de los conjuntos de las excepciones potenciales de todo lo que llamarían: constructores, operadores de asignación, destructores de datos miembro no variantes, no estáticos, bases directas, y, si es apropiado, clases base virtuales (incluyendo las expresiones de argumentos por defecto, 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, en otro caso, es la unión de todos los conjuntos de excepciones potenciales de todas las subexpresiones inmediatas de e (incluyendo las expresiones de argumentos por defecto), combinado con otro conjunto que depende de la forma de e, de la manera siguiente:

1) Si e es una expresión de llamada a función, y sea g la función, el puntero a función o el puntero a la función miembro que se llama, entonces
  • si la declaración de g usa una especificación dinámica de excepción, el conjunto de excepciones potenciales de g se añade al conjunto;
  • si la declaración de g usa noexcept(true), el conjunto está vacío;
(desde C++11)
  • de otra manera, el conjunto es el conjunto de todos los tipos.
2) Si e llama implícitamente a una función (es una expresión de operador y el operador está sobrecargado, es una expresión new y la función de asignación de memoria está sobrecargada, o es una expresión completa y se llama al destructor de un objeto temporal), entonces el conjunto es el conjunto de esa función.
3) Si e es una expresión throw, el conjunto es la excepción que se inicializaría por su operando, o el conjunto de todos los tipos para volver a lanzar la expresión throw (sin operando).
4) Si e es una conversión dinámica a una referencia a un tipo polimorfo, el conjunto consiste en std::bad_cast.
5) Si e es un typeid aplicado a un puntero desreferenciado a un tipo polimorfo, el conjunto consiste en std::bad_typeid.
6) Si e es una expresión new con un tamaño de array no constante, y la función de asignación seleccionada tiene un conjunto no vacío de posibles excepciones, 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()" es el conjunto 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 heredables (desde 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 excepción implícita permite todas la excepciones (la especificación de excepción se considera presente, aunque no se puede expresar en el código y se comporta como si no hubiera ninguna especificación de excepción) (hasta C++11)es noexcept(false) (desde C++11).
  • De otra forma, si el conjunto de excepciones potenciales no está vacío, la especificación de excepciones implícita lista todos los tipos del conjunto.
  • De otra manera, 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 generar una excepción de un tipo que concida con un controlador de tipo
    // std::bad_array_new_length, pero no genera una excepción de asignación incorrecta
    int * p = 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);
};


[editar] Ejemplo

Nota: es mejor compilar el modo C++98 para evitar avisos. Incompatible con C++17 y versiones posteriores.

#include <iostream>
#include <exception>
#include <cstdlib>
 
class X {};
class Y {};
class Z : public X {};
class W {};
 
void f() throw(X, Y) 
{
    bool n = false;
    if (n) throw X(); // de acuerdo, llamaría a std::terminate()
    if (n) throw Z(); // también de acuerdo
    throw W(); // llamará a std::unexpected()
}
 
void handler()
{
    std::cerr << "¡No se esperaba!\n"; // necesita vaciarse
    std::abort();
}
 
int main() {
  std::set_unexpected(handler);
  f();
}

Salida:

¡No se esperaba!

[editar] Informes de defectos

Los siguientes informes de defectos de cambio de comportamiento se aplicaron de manera retroactiva a los estándares de C++ publicados anteriormente.

ID Aplicado a Comportamiento según lo 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
se aplica la restricción
para punteros a funciones
y referencias
CWG 973 C++98 la especificación de excepción puede contener tipos de funciones, pero
no se especificó la conversión del puntero de función correspondiente
especificado
CWG 1267 C++11 Se permitían tipos referencia a rvalue 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 tiene en cuenta
CWG 1777 C++11 throw(T...) no era una especificación de no lanzamiento incluso
si T es una paquete vacío
no se lanza excepción si
el paquete está vacío
CWG 2191 C++98 el conjunto de posibles excepciones de una expresión typeid
podía contener bad_typeid incluso si no puede lanzar
sólo contiene bad_typeid
si se puede lanzar excepción

[editar] Véase también

Especificador noexcept Requiere que una función no lance excepciones (C++11)[editar]