Los especificadores de excepciones quedaron obsoletos porque los especificadores de excepciones 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 opción binaria: funciones que arrojarán y funciones que no lanzará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 en tiempo de compilación se resuelve en un booleano. Si el booleano es verdadero, entonces se noexcept
pega. Si el booleano es falso, entonces noexcept
no se pega y la función puede lanzar.
Por lo tanto, puede hacer algo como esto:
struct<typename T>
{
void CreateOtherClass() { T t{}; }
};
¿ CreateOtherClass
Lanza excepciones? Podría, si T
el 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()
.
noexcept
puede incurrir en comprobaciones de tiempo de ejecución. La principal diferencia entre ellos es que rompernoexcept
causasstd::terminate
y romperthrow
causasstd::unexpected
. También un comportamiento de desenrollado de pila ligeramente diferente en estos casos.