Lanzar palabra clave en la firma de la función

¿Cuál es el motivo técnico por el que se considera una mala práctica utilizar la palabra clave C ++ throw en una firma de función?

 bool some_func() throw(myExc) { ... if (problem_occurred) { throw myExc("problem occurred"); } ... } 

No, no se considera una buena práctica. Por el contrario, generalmente se considera una mala idea.

http://www.gotw.ca/publications/mill22.htm entra en muchos más detalles sobre por qué, pero el problema es en parte que el comstackdor no puede hacer cumplir esto, por lo que debe verificarse en tiempo de ejecución, que generalmente es indeseable. Y no está bien soportado en ningún caso. (MSVC ignora las especificaciones de excepción, excepto throw (), que interpreta como una garantía de que no se lanzará ninguna excepción.

Jalf ya está vinculado a él, pero el GOTW lo explica muy bien por qué las especificaciones de excepción no son tan útiles como cabría esperar:

 int Gunc() throw(); // will throw nothing (?) int Hunc() throw(A,B); // can only throw A or B (?) 

Son los comentarios correctos? No exactamente. Gunc() puede arrojar algo, y Hunc() bien puede lanzar algo que no sea A o B! El comstackdor simplemente garantiza golpearlos sin sentido si lo hacen … ah, y para golpear tu progtwig sin sentido también, la mayoría del tiempo.

Eso es exactamente lo que se reduce, es probable que termine con una llamada para terminate() y su progtwig muera de una muerte rápida pero dolorosa.

La conclusión de GOTW es:

Así que aquí está lo que parece ser el mejor consejo que hemos aprendido como comunidad a partir de hoy:

  • Moral # 1: Nunca escriba una especificación de excepción.
  • Moral # 2: Excepto posiblemente uno vacío, pero si yo fuera tú, evitaría incluso eso.

Para agregar un poco más de valor a todas las otras respuestas a esta pregunta, uno debe invertir unos minutos en la pregunta: ¿Cuál es el resultado del siguiente código?

 #include  void throw_exception() throw(const char *) { throw 10; } void my_unexpected(){ std::cout << "well - this was unexpected" << std::endl; } int main(int argc, char **argv){ std::set_unexpected(my_unexpected); try{ throw_exception(); }catch(int x){ std::cout << "catch int: " << x << std::endl; }catch(...){ std::cout << "catch ..." << std::endl; } } 

Respuesta: Como se indica aquí , el progtwig llama a std::terminate() y, por lo tanto, no se llamará a ninguno de los manejadores de excepciones.

Detalles: se llama a la función my_unexpected() pero, como no se vuelve a lanzar un tipo de excepción coincidente para el prototipo de función throw_exception() , al final se llama a std::terminate() . Entonces, la salida completa se ve así:

user @ user: ~ / tmp $ g ++ -o except.test except.test.cpp
usuario @ usuario: ~ / tmp $ ./except.test
bueno, esto fue inesperado
finalizar llamada después de lanzar una instancia de 'int'
Abortado (núcleo objeto de dumping)

El único efecto práctico del especificador de tiro es que si su función arroja algo diferente de myExc se myExc std::unexpected (en lugar del mecanismo de excepción no controlado normal).

Para documentar el tipo de excepciones que puede arrojar una función, normalmente hago esto:

 bool some_func() /* throw (myExc) */ { } 

Cuando se agregaron las especificaciones de lanzamiento al idioma, fue con las mejores intenciones, pero la práctica ha confirmado un enfoque más práctico.

Con C ++, mi regla general es usar especificaciones de tiro para indicar que un método no puede lanzar. Esta es una gran garantía. De lo contrario, supongamos que podría arrojar algo.

Bueno, al buscar en Google esta especificación de lanzamiento, eché un vistazo a este artículo: – ( http://blogs.msdn.com/b/larryosterman/archive/2006/03/22/558390.aspx )

También estoy reproduciendo una parte de ella aquí, para que pueda ser utilizada en el futuro independientemente del hecho de que el enlace de arriba funcione o no.

  class MyClass { size_t CalculateFoo() { : : }; size_t MethodThatCannotThrow() throw() { return 100; }; void ExampleMethod() { size_t foo, bar; try { foo = CalculateFoo(); bar = foo * 100; MethodThatCannotThrow(); printf("bar is %d", bar); } catch (...) { } } }; 

Cuando el comstackdor ve esto, con el atributo “throw ()”, el comstackdor puede optimizar completamente la variable “bar”, porque sabe que no hay forma de lanzar una excepción desde MethodThatCannotThrow (). Sin el atributo throw (), el comstackdor tiene que crear la variable “bar”, porque si MethodThatCannotThrow arroja una excepción, el manejador de excepciones puede / dependerá del valor de la variable de barra.

Además, las herramientas de análisis de código fuente como prefast pueden (y usarán) la anotación throw () para mejorar sus capacidades de detección de errores, por ejemplo, si tiene un try / catch y todas las funciones que llama están marcadas como throw (), no necesita el try / catch (sí, esto tiene un problema si luego llama a una función que podría lanzar).

Una especificación de no lanzamiento en una función en línea que solo devuelve una variable miembro y posiblemente no podría arrojar excepciones puede ser utilizada por algunos comstackdores para pesimizations (lo contrario de las optimizaciones) que pueden tener un efecto perjudicial sobre el rendimiento. Esto se describe en la literatura de Boost: Excepción-especificación

Con algunos comstackdores, una especificación de no lanzamiento en las funciones no en línea puede ser beneficiosa si se realizan las optimizaciones correctas y el uso de esa función afecta el rendimiento de una manera que lo justifique.

Para mí, parece que si usarlo o no es una llamada hecha por un ojo muy crítico como parte de un esfuerzo de optimización del rendimiento, tal vez usando herramientas de creación de perfiles.

Una cita del enlace de arriba para aquellos que tienen prisa:

Justificación de especificación de excepción

Las especificaciones de excepción [ISO 15.4] a veces se codifican para indicar qué excepciones se pueden lanzar, o porque el progtwigdor espera que mejoren el rendimiento. Pero considere el siguiente miembro desde un puntero inteligente:

T & operator * () const throw () {return * ptr; }

Esta función no llama a otras funciones; solo manipula tipos de datos fundamentales, como punteros. Por lo tanto, nunca se puede invocar el comportamiento en tiempo de ejecución de la especificación de excepción. La función está completamente expuesta al comstackdor; de hecho, se declara en línea. Por lo tanto, un comstackdor inteligente puede deducir fácilmente que las funciones son incapaces de lanzar excepciones, y hacer las mismas optimizaciones que habría hecho en base a la especificación de excepción vacía. Un comstackdor “tonto”, sin embargo, puede hacer todo tipo de pesimizaciones.

Por ejemplo, algunos comstackdores se apagan en línea si hay una especificación de excepción. Algunos comstackdores agregan bloques try / catch. Tales pesimizaciones pueden ser un desastre de rendimiento que hace que el código no se pueda usar en aplicaciones prácticas.

Aunque inicialmente atractivo, una especificación de excepción tiende a tener consecuencias que requieren una comprensión muy cuidadosa para comprender. El mayor problema con las especificaciones de excepción es que los progtwigdores las usan como si tuvieran el efecto deseado por el progtwigdor, en lugar del efecto que realmente tienen.

Una función no en línea es el único lugar donde la especificación de una excepción “no arroja nada” puede tener algún beneficio con algunos comstackdores.