c++ function function-try-block

c++ - ¿Cuándo es útil una función try block?



function function-try-block (6)

Me pregunto cuándo los programadores usan bloques de prueba de función. ¿Cuándo es útil?

void f(int i) try { if ( i < 0 ) throw "less than zero"; std::cout << "greater than zero" << std::endl; } catch(const char* e) { std::cout << e << std::endl; } int main() { f(1); f(-1); return 0; }

Salida: (en ideone )

greater than zero less than zero

EDITAR: Como algunas personas pueden pensar que la sintaxis de la definición de función es incorrecta (porque la sintaxis no parece familiar), debo decir que no es incorrecta. Se llama function-try-block. Ver §8.4 / 1 [dcl.fct.def] en el Estándar C ++.


Además de los usos funcionales mencionados, puede usar la función-try-block para ahorrarse un nivel de sangría. (Ack, una respuesta sobre estilos de codificación!)

Por lo general, ve ejemplos con la función-try-block así:

void f(/*...*/) try { /*...*/ } catch(/*...*/) { /*...*/ }

Donde el alcance de la función está sangrado al mismo nivel que si no hubiera función-try-block. Esto puede ser útil cuando:

  • tienes un límite de columna de 80 caracteres y tendrías que ajustar las líneas dada la sangría adicional.
  • Está intentando actualizar algunas funciones existentes con try catch y no quiere tocar todas las líneas de la función. (Sí, podríamos usar git blame -w .)

Sin embargo, para las funciones que están completamente envueltas en un function-try-block, sugiero no alternar entre algunas funciones usando function-try-blocks y algunas que no están dentro de la misma base de código. La consistencia es probablemente más importante que los problemas de ajuste de línea. :)


El bloque de prueba de función es útil para mí en dos contextos.

a) Tener una cláusula catch all alrededor de main() permita escribir pequeñas utilidades sin tener que preocuparse por el manejo local de errores:

int main() try { // ... return 0; } catch (...) { // handle errors return -1; }

que es claramente solo azúcar sintáctico para tener un try / catch dentro de main() .

b) manejar excepciones lanzadas por los constructores de la clase base:

struct B { B() { /*might throw*/ } }; struct A : B { A() try : B() { // ... } catch (...) { // handle exceptions thrown from inside A() or by B() } };


Lo usas en constructores para detectar errores de los inicializadores. Por lo general, no captas esos errores, por lo que este es un uso bastante excepcional.

De lo contrario, es inútil: a menos que se demuestre que estoy equivocado,

void f() try { ... } catch (...) { ... }

es estrictamente equivalente a

void f() { try { ... } catch (...) { ... } }


Notas sobre cómo funcionan los bloques try try:

  • Para los constructores, un bloque try de función abarca la construcción de miembros de datos y clases base.

  • Para los destructores, un bloque try de función abarca la destrucción de miembros de datos y clases base. Se vuelve complicado, pero para C ++ 11, debe incluir noexcept(false) en la declaración de su destructor (o el de una clase base / miembro) o cualquier excepción de destrucción dará como resultado la finalización al final del bloque catch. . Es posible evitar esto al poner una declaración de return en el bloque catch (pero esto no funcionará para los constructores).

  • Un bloque catch en un constructor o destructor debe arrojar alguna excepción (o volverá a lanzar implícitamente la excepción capturada) . No es legal simplemente return (al menos en el bloque catch de función del constructor). Sin embargo, tenga en cuenta que puede llamar a exit() o similar, lo que podría tener sentido en algunas situaciones.

  • Un bloque de catch no puede devolver un valor, por lo que no funciona para las funciones que devuelven non-void (a menos que finalicen intencionalmente el programa con exit() o similar). Al menos eso es lo que he leído .

  • El bloque catch para un constructor-function-try no puede hacer referencia a los miembros de base / datos ya que tendrán 1) no se han construido o 2) se han destruido antes de la captura. Como tal, los bloques de prueba de función no son útiles para limpiar el estado interno de un objeto; el objeto ya debería estar completamente "muerto" para cuando llegue allí. Este hecho hace que sea muy peligroso usar bloques de prueba de función en los constructores, ya que es difícil vigilar esta regla a lo largo del tiempo si su compilador (s) no lo marca.

usos válidos (legales)

  • Traducir una excepción (a un tipo / mensaje diferente) arrojada durante el constructor o sus constructores base / miembro.
  • Traducir o absorber y excepción lanzada durante el destructor o sus destructores de base / miembro (a pesar de la etiqueta del destructor ).
  • Terminar un programa (quizás con un mensaje útil).
  • Algún tipo de esquema de registro de excepciones.
  • Azúcar sintáctico para funciones de retorno de vacío que necesitan un bloque try / catch completamente encapsulado.

Otra cosa que puede usar es proporcionar datos adicionales durante la depuración, de una manera que no interfiera con la compilación terminada. No he visto a nadie más usarlo o defenderlo, pero es algo que considero conveniente.

// Function signature helper. #if defined(_WIN32) || defined(_WIN64) #define FUNC_SIG __FUNCSIG__ #elif defined(__unix__) #define FUNC_SIG __PRETTY_FUNCTION__ // Add other compiler equivalents here. #endif /* Function signature helper. */ void foo(/* whatever */) #ifdef DEBUG try #endif /* DEBUG */ { // ... } #ifdef DEBUG catch(SomeExceptionOrOther& e) { std::cout << "Exception " << e.what() << std::endl << "* In function: " << FUNC_SIG << std::endl << "* With parameters: " << /* output parameters */ << std::endl << "* With internal variables: " << /* output vars */ << std::endl; throw; } #endif /* DEBUG */

Esto le permitiría obtener información útil mientras prueba su código y fácilmente simularlo sin afectar nada.


Puede ser útil si desea capturar excepciones del inicializador del constructor.

Sin embargo, si atrapas excepciones en el constructor de esa manera, tienes que volver a lanzarlo o lanzar una nueva excepción (es decir, no puedes regresar normalmente desde el constructor). Si no vuelves a lanzar, simplemente sucede implícitamente.

#include <iostream> class A { public: A() try { throw 5; } catch (int) { std::cout << "exception thrown/n"; //return; <- invalid } }; int main() { try { A a; } catch (...) { std::cout << "was rethrown"; } }