¿Cuál es la diferencia entre el especificador `throw()` de C++03 y `noexcept` de C++11?

5 minutos de lectura

avatar de usuario de iammilind
iammilind

¿Hay alguna diferencia entre throw() y noexcept además de ser verificado en tiempo de ejecución y tiempo de compilación respectivamente?

Este artículo de Wikipedia C++11 sugiere que los especificadores de lanzamiento de C++03 están en desuso.
¿Por qué tan… es el noexcept lo suficientemente capaz como para cubrir todo eso en tiempo de compilación?


Nota: Revisé esta pregunta y Este artículopero no pudo determinar el motivo sólido de su desaprobación.

  • De acuerdo a esto Buen articulo además noexcept puede incurrir en controles de tiempo de ejecución. La principal diferencia entre ellos es que romper noexcept causas std::terminate mientras se rompe throw causas std::unexpected. También un comportamiento de desenrollado de pila ligeramente diferente en estos casos.

    – Fiktik

    11 de octubre de 2012 a las 6:17


  • No hay nada de “tiempo de compilación” verificado con algunas especificaciones de excepción que sea “tiempo de ejecución” verificado en otros. Es solo un mito creado por los oponentes de las especificaciones de excepción de C++.

    – chico curioso

    1 de noviembre de 2019 a las 8:12

Avatar de usuario de Nicol Bolas
Nicolás Bolas

Los especificadores de excepción quedaron en desuso porque los especificadores de excepción son generalmente una idea terrible. noexcept se agregó porque es el único uso razonablemente útil de un especificador de excepción: saber cuándo una función no lanzar una excepción. Por lo tanto, se convierte en una elección binaria: funciones que arrojarán y funciones que no arrojarán.

noexcept se agregó en lugar de simplemente eliminar todos los especificadores de lanzamiento que no sean throw() porque noexcept es más poderoso noexcept puede tener un parámetro que se resuelve en tiempo de compilación en un valor booleano. Si el booleano es verdadero, entonces el noexcept palos Si el booleano es falso, entonces el noexcept no se pega y la función puede fallar.

Por lo tanto, puedes hacer algo como esto:

struct<typename T>
{
  void CreateOtherClass() { T t{}; }
};

Lo hace CreateOtherClass lanzar excepciones? Podría, si TEl constructor predeterminado de can. ¿Cómo lo decimos? Me gusta esto:

struct<typename T>
{
  void CreateOtherClass() noexcept(is_nothrow_default_constructible<T>::value) { T t{}; }
};

Por lo tanto, CreateOtherClass() arrojará si el constructor predeterminado del tipo dado arroja. Esto soluciona uno de los principales problemas con los especificadores de excepción: su incapacidad para propagarse en la pila de llamadas.

no puedes hacer esto con throw().

  • +1 Respuesta útil, para mí de todos modos. Sigo buscando una respuesta que diga por qué querría usar noexcept. nunca usé throw() especificador, alguna vez y estoy tratando de determinar si noexcept en realidad proporciona algún beneficio (aparte de la documentación verificada por el compilador).

    – hmjd

    20 de febrero de 2013 a las 14:16


  • Sí, lo sé. solo estaba investigando noexcept y leyendo algunas preguntas/respuestas al respecto. La razón más importante que encontré fue que algunos (posiblemente todos) de los contenedores STL no usarán el constructor de movimiento si no se declara como noexceptque no me di cuenta.

    – hmjd

    20 de febrero de 2013 a las 21:11

  • @NicolBolas está de acuerdo. pero si noexcept fuera una garantía, el compilador podría verificar si una función puede arrojar o no un destructor. Pudiendo así advertir a un programador que una función es noexcept o no.

    – Alejandro Ah

    26 de agosto de 2013 a las 10:47

  • @NicolBolas las llamadas en tiempo de ejecución std::terminate. cual es MUCHO PEOR! el código puede colarse en versiones que tienen funciones marcadas noexcept y en tiempo de ejecución (es decir, en los sitios de los clientes) se detectan infracciones. Quise decir que el compilador garantiza generar código que no tirar excepciones en primer lugar.

    – Alejandro Ah

    26 de agosto de 2013 a las 11:58

  • @NicolBolas: Otra diferencia que vale la pena señalar. Si una función está marcada throws() entonces, si se lanza una excepción, la pila debe ser desenrollado hasta el alcance de esa función (por lo que todas las variables automáticas en la función se destruyen) en cuyo punto terminate() se llama (a través de unexpected()). Si una función está marcada noexcept luego, si se lanza una excepción, se llama a terminar (el desenrollado de la pila es un detalle definido por la implementación).

    – Martín York

    17/09/2013 a las 11:39


noexcept no se comprueba en tiempo de compilación.

Una implementación no debe rechazar una expresión simplemente porque cuando se ejecuta arroja o podría arrojar una excepción que la función contenedora no permite.

Cuando una función que se declara noexcept o throw() intenta lanzar una excepción, la única diferencia es que uno llama terminate y las otras llamadas unexpected y el último estilo de manejo de excepciones ha quedado efectivamente en desuso.

  • Pero si una función virtual tiene throw()/noexceptla verificación del tiempo de compilación garantiza que también haya un anulador.

    – chico curioso

    28 de octubre de 2019 a las 4:56

avatar de usuario de ma13
ma13

std::unexpected() es llamado por el tiempo de ejecución de C++ cuando se viola una especificación de excepción dinámica: se lanza una excepción desde una función cuya especificación de excepción prohíbe las excepciones de este tipo.

std::unexpected() también se puede llamar directamente desde el programa.

En cualquier caso, std::unexpected llama al actualmente instalado std::unexpected_handler. El valor por defecto std::unexpected_handler llamadas std::terminate.

¿Ha sido útil esta solución?