Especificación de excepción dinámica (hasta C++17)
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) | |||||||
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:
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.(desde C++11) |
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:
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 deg
se añade al conjunto;
- si la declaración de
|
(desde C++11) |
- de otra manera, el conjunto es el conjunto de todos los tipos.
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.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).e
es una conversión dinámica a una referencia a un tipo polimorfo, el conjunto consiste en std::bad_cast.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) |