¿Debo usar un especificador de excepciones en C++?


En C++, puede especificar que una función puede o no lanzar una excepción utilizando un especificador de excepción. Por ejemplo:

void foo() throw(); // guaranteed not to throw an exception
void bar() throw(int); // may throw an exception of type int
void baz() throw(...); // may throw an exception of some unspecified type

Tengo dudas sobre su uso debido a lo siguiente:

  1. El compilador realmente no aplica especificadores de excepción de manera rigurosa, por lo que los beneficios no son grandes. Idealmente, le gustaría obtener un error de compilación.
  2. Si una función viola un especificador de excepción, creo que el comportamiento estándar es terminar el programa.
  3. En VS.Net, trata throw (X) como throw(...), por lo que la adhesión a la norma no es fuerte.

¿Cree que deberían usarse especificadores de excepciones?
Por favor responda con "sí" o "no" y proporcione algunas razones para justificar su respuesta.

Author: iammilind, 2008-09-18

14 answers

No.

Aquí hay varios ejemplos de por qué:

  1. El código de plantilla es imposible de escribir con las especificaciones de excepción,

    template<class T>
    void f( T k )
    {
         T x( k );
         x.x();
    }
    

    Las copias pueden lanzar, el parámetro que pasa puede lanzar, y x() puede lanzar alguna excepción desconocida.

  2. Las especificaciones de excepción tienden a prohibir la extensibilidad.

    virtual void open() throw( FileNotFound );
    

    Podría evolucionar a

    virtual void open() throw( FileNotFound, SocketNotReady, InterprocessObjectNotImplemented, HardwareUnresponsive );
    

    Realmente podrías escribir eso como

    throw( ... )
    

    El primero no es extensible, el segundo es demasiado ambicioso y el tercero es realmente lo que quieres decir, cuando escribes funciones virtuales.

  3. Código heredado

    Cuando escribes código que se basa en otra biblioteca, realmente no sabes lo que podría hacer cuando algo sale terriblemente mal.

    int lib_f();
    
    void g() throw( k_too_small_exception )
    { 
       int k = lib_f();
       if( k < 0 ) throw k_too_small_exception();
    }
    

    g terminará, cuando lib_f() lanza. Esto no es (en la mayoría de los casos) lo que realmente quieres. std::terminate() nunca debe ser llamado. Siempre es mejor dejar que la aplicación se bloquee con una excepción no controlada, de la que se puede recuperar una pila-trace, que en silencio/morir violentamente.

  4. Escribe código que devuelve errores comunes y lanza en ocasiones excepcionales.

    Error e = open( "bla.txt" );
    if( e == FileNotFound )
        MessageUser( "File bla.txt not found" );
    if( e == AccessDenied )
        MessageUser( "Failed to open bla.txt, because we don't have read rights ..." );
    if( e != Success )
        MessageUser( "Failed due to some other error, error code = " + itoa( e ) );
    
    try
    {
       std::vector<TObj> k( 1000 );
       // ...
    }
    catch( const bad_alloc& b )
    { 
       MessageUser( "out of memory, exiting process" );
       throw;
    }
    

Sin embargo, cuando su biblioteca simplemente lanza sus propias excepciones, puede usar especificaciones de excepción para indicar su intención.

 88
Author: Christopher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-06-23 12:55:01

Evite las especificaciones de excepción en C++. Las razones que usted da en su pregunta son un buen comienzo para saber por qué.

Véase Herb Sutter "A Pragmatic Look at Exception Specifications".

 42
Author: Michael Burr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-14 08:39:42

Creo que la convención standard except (para C++)
Los especificadores de excepción fueron un experimento en el estándar de C++ que en su mayoría falló.
La excepción es que el especificador no throw es útil, pero también debe agregar internamente el bloque try catch apropiado para asegurarse de que el código coincida con el especificador. Herb Sutter tiene una página sobre el tema. Gotch 82

En una adición creo que vale la pena describir las Garantías de Excepción.

Estos son básicamente documentación sobre cómo el estado de un objeto se ve afectado por las excepciones que escapan de un método en ese objeto. Desafortunadamente, el compilador no las hace cumplir ni las menciona.
Boost y excepciones

Garantías de excepción

No hay garantía:

No hay garantía sobre el estado del objeto después de que una excepción escape de un método
En estas situaciones, el objeto ya no debe utilizarse.

Garantía básica:

En casi todas las situaciones esta debe ser la garantía mínima que proporciona un método.
Esto garantiza que el estado del objeto está bien definido y puede ser utilizado de manera consistente.

Fuerte Garantía: (también conocida como Garantía Transaccional)

Esto garantiza que el método se completará correctamente
O se lanzará una excepción y el estado de los objetos no cambiará.

No Hay Garantía de Tiro:

El método garantiza que no se permiten excepciones para propagarse fuera del método.
Todos los destructores deben hacer esta garantía.
/ N.b. If an exception escapes a un destructor while an exception is already propagating
/ la solicitud terminará

 14
Author: Martin York,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-25 10:18:56

Gcc emitirá advertencias cuando viole las especificaciones de excepción. Lo que hago es usar macros para usar las especificaciones de excepción solo en un modo de compilación "lint" expresamente para verificar que las excepciones estén de acuerdo con mi documentación.

 8
Author: Jeremy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-18 03:26:36

El único especificador de excepción útil es "throw()", como en "doesn't throw".

 7
Author: Harold Ekstrom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-18 20:29:26

No. Si los usa y se lanza una excepción que no especificó, ya sea por su código o código llamado por su código, entonces el comportamiento predeterminado es terminar rápidamente su programa.

Además, creo que su uso ha sido obsoleto en los borradores actuales del estándar C++0x.

 4
Author: Ferruccio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-19 01:49:04

Las especificaciones de excepción no son herramientas maravillosamente útiles en C++. Sin embargo, hay /es/ un buen uso para ellos, si se combina con std::unexpected.

Lo que hago en algunos proyectos es codificar con especificaciones de excepción, y luego llamar a set_unexpected() con una función que lanzará una excepción especial de mi propio diseño. Esta excepción, tras la construcción, obtiene una traza inversa (de una manera específica de la plataforma) y se deriva de std::bad_exception (para permitir que se propague si se desea). Si causa una llamada terminate (), como suele hacer, la traza inversa se imprime por what () (así como la excepción original que la causó; no es difícil encontrarlo) y así obtengo información de dónde se violó mi contrato, como qué excepción inesperada de la biblioteca se lanzó.

Si hago esto, nunca permito la propagación de excepciones de bibliotecas (excepto las std) y derivo todas mis excepciones de std::exception. Si una biblioteca decide lanzar, atraparé y convertiré en mi propia jerarquía, lo que me permite controlar siempre el código. Las funciones templadas que llaman a funciones dependientes deben evitar las especificaciones de excepción por razones obvias; pero es raro tener una interfaz de función templada con código de biblioteca de todos modos (y pocas bibliotecas realmente usan plantillas de una manera útil).

 4
Author: coppro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-20 19:17:40

Si está escribiendo código que será utilizado por personas que prefieren mirar la declaración de la función que cualquier comentario a su alrededor, entonces una especificación les dirá qué excepciones podrían querer capturar.

De lo contrario, no encuentro particularmente útil usar nada más que throw() para indicar que no arroja ninguna excepción.

 3
Author: Branan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-17 23:13:54

Una especificación "throw()" permite al compilador realizar algunas optimizaciones al hacer análisis de flujo de código si sabe que la función nunca lanzará una excepción (o al menos promete nunca lanzar una excepción). Larry Osterman habla de esto brevemente aquí:

Http://blogs.msdn.com/larryosterman/archive/2006/03/22/558390.aspx

 3
Author: TheJuice,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-01 15:13:31

Generalmente no usaría especificadores de excepciones. Sin embargo, en los casos en que cualquier otra excepción provenga de la función en cuestión que el programa definitivamente sería incapaz de corregir, entonces puede ser útil. En todos los casos, asegúrese de documentar claramente qué excepciones se pueden esperar de esa función.

Sí, el comportamiento esperado de una excepción no especificada lanzada desde una función con especificadores de excepción es llamar a terminate().

I también notará que Scott Meyers aborda este tema en C++Más Efectivo. Su C++ Efectivo y C++ Más Efectivo son libros muy recomendados.

 2
Author: Kris Kumler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-17 23:12:59

Sí, si te gusta la documentación interna. O tal vez escribir un libario que otros utilizarán, para que puedan decir lo que sucede sin consultar la documentación. Lanzar o no lanzar puede considerarse parte de la API, casi como el valor devuelto.

Estoy de acuerdo, no son realmente útiles para hacer cumplir el estilo Java de corrección en el compilador, pero es mejor que nada o comentarios al azar.

 2
Author: user10392,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-17 23:17:13

Pueden ser útiles para pruebas unitarias para que al escribir pruebas sepa qué esperar que la función lance cuando falla, pero no hay ninguna aplicación que las rodee en el compilador. Creo que son código extra que no es necesario en C++. Lo único que debes estar seguro es que sigues el mismo estándar de codificación en todo el proyecto y los miembros del equipo para que tu código siga siendo legible.

 2
Author: Odd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-09-17 23:26:58

Del artículo:

Http://www.boost.org/community/exception_safety.html

" Es bien sabido que es imposible escribir una excepción - safe generic contenedor."Esta afirmación se escucha a menudo con referencia a un artículo de Tom Cargill [4] en el que explora la problema de excepción-seguridad para un plantilla de pila genérica. En su artículo, Cargill plantea muchos útiles preguntas, pero lamentablemente no presentar una solución a su problema.1 He concluye sugiriendo que la solución puede no ser posible. Desafortunadamente, su artículo fue leído por muchos como "prueba" de esa especulación. Desde que se publicó ha habido muchos ejemplos de exception-safe componentes genéricos, entre ellos el C++ contenedores de biblioteca estándar.

Y, de hecho, puedo pensar en formas de hacer que la excepción de clases de plantilla sea segura. A menos que usted no tenga control sobre todas las sub-clases entonces usted puede tener un problema de todos modos. Para hacer esto se podría crear typedefs en sus clases que definen las excepciones lanzadas por varias clases de plantilla. Esto creo que el problema es como siempre clavarlo después en lugar de diseñarlo desde el principio, y creo que es esta sobrecarga que es el verdadero obstáculo.

 0
Author: Marius,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-21 17:11:39

Especificaciones de excepción = basura, pregunte a cualquier desarrollador de Java mayor de 30

 -1
Author: Greg Dean,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2008-10-20 19:22:28