noexcept
specifier
(since C++11)
Especifica si una función puede lanzar excepciones.
Contenidos |
Sintaxis
noexcept
|
(1) | ||||||||
noexcept(
expression
)
|
(2) | ||||||||
throw()
|
(3) |
(obsoleto en C++17)
(eliminado en C++20) |
|||||||
noexcept(true)
(
que sigue a
noexcept
siempre es parte de esta forma (nunca puede iniciar un inicializador).
noexcept(true)
(ver
especificación de excepciones dinámicas
para su semántica antes de C++17)
| expression | - | expresión constante convertida contextualmente de tipo bool |
Explicación
|
La especificación noexcept no es parte del tipo de función (al igual que la especificación de excepción dinámica ) y solo puede aparecer como parte de un declarador lambda o un declarador de función de nivel superior al declarar funciones, variables, miembros de datos no estáticos de tipo función, puntero a función, referencia a función, o puntero a función miembro, y también al declarar un parámetro o tipo de retorno en una de esas declaraciones que a su vez resulta ser un puntero o referencia a función. No puede aparecer en una declaración de typedef o alias de tipo . void f() noexcept; // la función f() no lanza excepciones void (*fp)() noexcept(false); // fp apunta a una función que puede lanzar excepciones void g(void pfa() noexcept); // g toma un puntero a función que no lanza excepciones // typedef int (*pf)() noexcept; // error |
(hasta C++17) |
|
La especificación noexcept es parte del tipo de función y puede aparecer como parte de cualquier declarador de función . |
(desde C++17) |
Toda función en C++ es no lanzadora o potencialmente lanzadora :
- funciones potencialmente-lanzadoras son:
|
(hasta C++17) |
-
-
funciones declaradas con el especificador
noexceptcuya expresión se evalúa comofalse -
funciones declaradas sin el especificador
noexceptexcepto
-
- destructores a menos que el destructor de cualquier base o miembro potencialmente construido sea potencialmente lanzador de excepciones (ver más abajo)
- constructores por defecto , constructores de copia , constructores de movimiento que están implícitamente declarados o definidos por defecto en su primera declaración a menos que
-
- un constructor para una base o miembro que la definición implícita del constructor llamaría sea potencialmente lanzador de excepciones (ver más abajo)
- una subexpresión de dicha inicialización, como una expresión de argumento por defecto, sea potencialmente lanzadora de excepciones (ver más abajo)
- un inicializador de miembro por defecto (solo para constructor por defecto) sea potencialmente lanzador de excepciones (ver más abajo)
- operadores de asignación de copia , operadores de asignación de movimiento que están implícitamente declarados o definidos por defecto en su primera declaración a menos que la invocación de cualquier operador de asignación en la definición implícita sea potencialmente lanzadora de excepciones (ver más abajo)
-
funciones declaradas con el especificador
|
(desde C++20) |
-
las funciones que no lanzan excepciones son todas las demás (aquellas con especificador noexcept cuya
expresión
se evalúa como
trueasí como los destructores, las funciones miembro especiales predeterminadas y las funciones de desasignación)
Instanciaciones explícitas pueden usar el especificador noexcept, pero no es obligatorio. Si se utiliza, la especificación de excepciones debe ser la misma que para todas las demás declaraciones. Solo se requiere un diagnóstico si las especificaciones de excepciones no son las mismas dentro de una única unidad de traducción.
Las funciones que solo difieren en su especificación de excepción no pueden sobrecargarse (al igual que el tipo de retorno, la especificación de excepción es parte del tipo de función, pero no parte de la firma de la función) (desde C++17) .
void f() noexcept; void f(); // error: especificación de excepción diferente void g() noexcept(false); void g(); // ok, ambas declaraciones para g son potencialmente lanzadoras
Los punteros (incluyendo punteros a función miembro) a funciones que no lanzan excepciones pueden asignarse a o usarse para inicializar (until C++17) son implícitamente convertibles a (since C++17) punteros a funciones potencialmente lanzadoras de excepciones, pero no a la inversa.
void ft(); // potencialmente lanzadora void (*fn)() noexcept = ft; // error
Si una función virtual no lanza excepciones, todas las declaraciones, incluyendo la definición, de cada sobreescritura deben ser no lanzadoras también, a menos que la sobreescritura esté definida como eliminada:
struct B { virtual void f() noexcept; virtual void g(); virtual void h() noexcept = delete; }; struct D: B { void f(); // incorrecto: D::f es potencialmente lanzador de excepciones, B::f es no lanzador void g() noexcept; // OK void h() = delete; // OK };
Se permiten funciones no lanzadoras llamar a funciones potencialmente lanzadoras. Siempre que se lance una excepción y la búsqueda de un manejador encuentre el bloque más externo de una función no lanzadora, se llama a la función std::terminate :
extern void f(); // potencialmente lanzadora de excepciones void g() noexcept { f(); // válido, incluso si f lanza excepciones throw 42; // válido, efectivamente una llamada a std::terminate }
La especificación de excepción 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 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 se instancie la especificación de excepción de una función miembro de la clase base).
Cuando la especificación noexcept 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 expresión se instancia como si fuera para la declaración de la especialización.
Una especificación noexcept 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() noexcept(sizeof(T) < 4); int main() { decltype(f<void>()) *p; // f no evaluada, pero se necesita la especificación noexcept // error porque la instanciación de la especificación noexcept // 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).
Definición formal de expresión potencialmente lanzadora (utilizada para determinar la especificación de excepción predeterminada de destructores, constructores y operadores de asignación como se describió anteriormente):
Una expresión
e
es
potencialmente-lanzadora
si:
-
ees una llamada a función a una función, puntero a función, o puntero a función miembro que es potencialmente-lanzadora , a menos queesea una expresión constante núcleo (hasta C++17) -
erealiza una llamada implícita a una función potencialmente-lanzadora (como un operador sobrecargado, una función de asignación en una expresiónnew, un constructor para un argumento de función, o un destructor siees una expresión completa) -
ees unathrow-expresión -
ees undynamic_castque convierte un tipo de referencia polimórfica -
ees una expresióntypeidaplicada a un puntero desreferenciado a un tipo polimórfico -
etiene una subexpresión inmediata que es potencialmente-lanzadora
struct A { A(int = (A(5), 0)) noexcept; A(const A&) noexcept; A(A&&) noexcept; ~A(); }; struct B { B() throw(); B(const B&) = default; // la especificación de excepción implícita es noexcept(true) B(B&&, int = (throw Y(), 0)) noexcept; ~B() noexcept(false); }; int n = 7; struct D : public A, public B { int * p = new int[n]; // D::D() potencialmente lanzador debido al operador new // D::D(const D&) no lanzador // D::D(D&&) potencialmente lanzador: el argumento por defecto del constructor de B puede lanzar // D::~D() potencialmente lanzador // nota: si A::~A() fuera virtual, este programa estaría mal formado porque un reemplazante // de un virtual no lanzador no puede ser potencialmente lanzador };
Notas
Uno de los usos de la expresión
constante
es (junto con el
noexcept
operador
) definir plantillas de función que declaran
noexcept
para algunos tipos pero no para otros.
Tenga en cuenta que una
noexcept
especificación en una función no es una verificación en tiempo de compilación; es simplemente un método para que el programador informe al compilador si una función debe lanzar excepciones o no. El compilador puede usar esta información para habilitar ciertas optimizaciones en funciones que no lanzan excepciones, así como para habilitar el
noexcept
operador
, que puede verificar en tiempo de compilación si una expresión particular está declarada para lanzar excepciones. Por ejemplo, contenedores como
std::vector
moverán sus elementos si el constructor de movimiento de los elementos es
noexcept
, y los copiará en caso contrario (a menos que el constructor de copia no sea accesible, pero sí un constructor de movimiento potencialmente lanzador de excepciones, en cuyo caso se renuncia a la garantía fuerte de excepciones).
Obsoleta
noexcept
es una versión mejorada de
throw
(
)
, que está obsoleta en C++11. A diferencia de
throw
(
)
anterior a C++17,
noexcept
no llamará a
std::unexpected
, puede o no desenrollar la pila, y llamará a
std::terminate
, lo que potencialmente permite al compilador implementar
noexcept
sin la sobrecarga en tiempo de ejecución de
throw
(
)
. A partir de C++17,
throw
(
)
se redefine para ser un equivalente exacto de
noexcept
(
true
)
.
| Macro de prueba de características | Valor | Std | Característica |
|---|---|---|---|
__cpp_noexcept_function_type
|
201510L
|
(C++17) | Hacer que las especificaciones de excepción sean parte del sistema de tipos |
Palabras clave
noexcept , throw (desde C++17) (hasta C++20)
Ejemplo
// whether foo is declared noexcept depends on if the expression // T() will throw any exceptions template<class T> void foo() noexcept(noexcept(T())) {} void bar() noexcept(true) {} void baz() noexcept { throw 42; } // noexcept is the same as noexcept(true) int main() { foo<int>(); // noexcept(noexcept(int())) => noexcept(true), so this is fine bar(); // fine baz(); // compiles, but at runtime this calls std::terminate }
Informes de defectos
Los siguientes informes de defectos que modifican el comportamiento se aplicaron retroactivamente a los estándares de C++ publicados anteriormente.
| DR | Se aplica a | Comportamiento publicado | Comportamiento correcto |
|---|---|---|---|
| CWG 1330 | C++11 | una especificación de excepción podría ser instanciada inmediatamente | solo se instancia si es necesario |
| CWG 1740 | C++11 | un ( después de noexcept podría iniciar un inicializador |
solo puede ser parte de
la especificación noexcept |
| CWG 2039 | C++11 | solo la expresión antes de la conversión debe ser constante |
la conversión también debe ser
válida en una expresión constante |
Véase también
noexcept
operador
(C++11)
|
determina si una expresión lanza alguna excepción |
| Especificación dinámica de excepciones (hasta C++17) | especifica qué excepciones son lanzadas por una función (obsoleto en C++11) |
throw
expresión
|
señala un error y transfiere el control al manejador de errores |
|
(C++11)
|
convierte el argumento a un xvalue si el constructor de movimiento no lanza excepciones
(plantilla de función) |