Dynamic exception specification (until C++17)
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) |
|||||||
| 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:
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) |
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:
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
gutiliza una especificación de excepción dinámica, el conjunto de excepciones potenciales degse añade al conjunto;
-
si la declaración de
|
(desde C++11) |
-
- de lo contrario, el conjunto es el conjunto de todos los tipos.
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.
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).
e
es un
dynamic_cast
a una referencia de un tipo polimórfico, el conjunto consiste en
std::bad_cast
.
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
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 |