Diferencia entre C ++ 03 throw () especificador C ++ 11 noexcept

100

¿Hay alguna diferencia entre throw()y noexceptademá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 obsoletos.
¿Por qué es lo noexceptsuficientemente capaz de cubrir todo eso en tiempo de compilación?

[Nota: Revisé esta pregunta y este artículo , pero no pude determinar la razón sólida de la desaprobación].

iammilind
fuente
7
De acuerdo con este bonito artículo, también noexceptpuede incurrir en comprobaciones de tiempo de ejecución. La principal diferencia entre ellos es que romper noexceptcausas std::terminatey romper throwcausas std::unexpected. También un comportamiento de desenrollado de pila ligeramente diferente en estos casos.
Fiktik
No hay nada "tiempo de compilación" verificado con algunas especificaciones de excepción que sea "tiempo de ejecución" verificado en otras. Es solo un mito creado por los oponentes de las especificaciones de excepción de C ++.
Curioso

Respuestas:

129

Los especificadores de excepciones quedaron obsoletos porque los especificadores de excepciones son generalmente una idea terrible . noexceptse 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 opción binaria: funciones que arrojarán y funciones que no lanzarán.

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

Por lo tanto, puede hacer algo como esto:

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

¿ CreateOtherClassLanza excepciones? Podría, si Tel constructor predeterminado puede. ¿Cómo lo contamos? Me gusta esto:

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

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

No puedes hacer esto con throw().

Nicol Bolas
fuente
+1 respuesta útil, para mí de todos modos. Todavía estoy buscando una respuesta que diga por qué querría usar noexcept. Nunca usé el throw()especificador, nunca y estoy tratando de determinar si noexceptrealmente proporciona algún beneficio (aparte de la documentación verificada por el compilador).
hmjd
Acabo de encontrar esto stackoverflow.com/questions/10787766/… ...
hmjd
1
@NicolBolas está de acuerdo. pero si no, excepto fuera una garantía, el compilador podría comprobar si una función puede lanzar o no un destructor. Pudiendo así advertir a un programador que una función no es excepto o no.
Alex
2
@NicolBolas las llamadas en tiempo de ejecución std::terminate. que es MUCHO PEOR ! el código puede colarse en las versiones que tienen funciones marcadas noexcept y en tiempo de ejecución (es decir, en los sitios del cliente) se detectan violaciones. Me refiero a que el compilador garantiza generar código que no arroje excepciones en primer lugar.
Alex
2
@NicolBolas: Otra diferencia que vale la pena señalar. Si se marca una función throws(), si se lanza una excepción, la pila debe desenrollarse hasta el alcance de esa función (por lo que todas las variables automáticas de la función se destruyen) en cuyo punto terminate()se llama (vía unexpected()). Si se marca una función noexcept, si se lanza una excepción, se llama a terminar (el desenrollado de la pila es un detalle definido por la implementación).
Martin York
33

noexcept no se comprueba en tiempo de compilación.

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

Cuando una función que se declara noexcepto throw()intenta lanzar una excepción, la única diferencia es que uno llama terminatey el otro llama unexpectedy el último estilo de manejo de excepciones ha quedado obsoleto.

CB Bailey
fuente
Pero si una función virtual tiene throw()/ noexcept, la verificación del tiempo de compilación asegura que un overrider también lo tenga.
Curioso
2

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 excepciones de este tipo.

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

En cualquier caso, std::unexpectedllama al archivo actualmente instalado std::unexpected_handler. Las std::unexpected_handlerllamadas predeterminadas std::terminate.

ma13
fuente