Namespaces
Variants

noexcept specifier (since C++11)

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)

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)
1) Igual que noexcept(true)
2) Si expression se evalúa como true , la función se declara para no lanzar ninguna excepción. Un ( que sigue a noexcept siempre es parte de esta forma (nunca puede iniciar un inicializador).
3) Igual que 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 noexcept cuya expresión se evalúa como false
  • funciones declaradas sin el especificador noexcept excepto
  • 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 comparación que se definen por defecto en su primera declaración a menos que la invocación de cualquier operador de comparación en la definición implícita sea potencialmente lanzadora de excepciones (ver más abajo)
(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 true así 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:

  • e es 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 que e sea una expresión constante núcleo (hasta C++17)
  • e realiza una llamada implícita a una función potencialmente-lanzadora (como un operador sobrecargado, una función de asignación en una expresión new , un constructor para un argumento de función, o un destructor si e es una expresión completa)
  • e es una throw -expresión
  • e es un dynamic_cast que convierte un tipo de referencia polimórfica
  • e es una expresión typeid aplicada a un puntero desreferenciado a un tipo polimórfico
  • e tiene 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
convierte el argumento a un xvalue si el constructor de movimiento no lanza excepciones
(plantilla de función)