c++ - aserciones - ¿Qué significa afirmar(0)?
static assert c++ (4)
Tuve una pregunta como esta en uno de mis exámenes y todavía no estoy seguro de cómo responderla. Entiendo que las afirmaciones son formas de probar su programa, sin embargo, no estoy muy seguro de lo que está comprobando assert(0)
. ¿Es esta una pregunta con trampa? Siempre fallará, pero no entiendo por qué. ¿Qué está comprobando?
Cualquier explicación sería genial, gracias.
El estándar C ++ difiere la definición de assert
al estándar C.
” La macro
assert
pone pruebas de diagnóstico en programas; se expande a una expresión vacía. Cuando se ejecuta, si la expresión (que tendrá un tipo escalar) es falsa (es decir, se compara igual a 0), la macro de afirmación escribe información sobre la llamada particular que falló (incluido el texto del argumento, el nombre del archivo de origen, el número de línea de origen y el nombre de la función__FILE__
; estos últimos son, respectivamente, los valores de las macros de preprocesamiento__FILE__
y__LINE__
y del identificador__func__
) en el archivo de error estándar en un formato definido por la implementación. Entonces llama a la funciónabort
.
En assert(0)
el 0
se interpreta como false
, por lo que esta afirmación siempre fallará o se activará cuando la verificación de afirmación esté activada.
Por lo tanto, afirma que
"La ejecución nunca llegará a este punto".
En la práctica, puede ser difícil hacer que los compiladores se callen cuando la ejecución llega o no a un punto determinado. A menudo, el compilador primero se quejará de la ejecución que posiblemente llegue al final de una función sin devolver un valor. Agregar un assert(0)
idealmente debería resolver ese problema, pero luego el compilador puede quejarse sobre el assert
, o no reconocer que dice que ya está al tanto de lo que trata de advertir.
Una (1) medida posible entonces es lanzar una excepción en ese punto:
auto foo( int x )
-> int
{
if( x == 1 ) { return 42; }
assert( 0 ); throw 0; // Should never get here!
}
Por supuesto, ese doble golpe puede definirse como una macro de nivel superior. Con respecto al tipo de excepción, es posible que desee mantenerla como no como una std::exception
, ya que esta no es una excepción que debe ser capturada por una catch
normal en cualquier lugar. O, si confías en la jerarquía de excepciones estándar (no tiene sentido para mí, pero) puedes usar std::logic_error
.
Para desactivar la verificación de aserción, puede definir el símbolo NDEBUG
antes de incluir <assert.h>
.
Este encabezado tiene un soporte especial para que pueda incluirlo varias veces, con o sin NDEBUG
definido.
” Una unidad de traducción puede incluir encabezados de biblioteca en cualquier orden (Cláusula 2). Cada uno puede incluirse más de una vez, sin que el efecto sea diferente de ser incluido exactamente una vez, excepto que el efecto de incluir
<cassert>
o<assert.h>
depende cada vez de la definición léxicamente actual deNDEBUG
.
Una definición razonable del doble golpe discutido anteriormente también puede depender de NDEBUG
sin incluir protección, por ejemplo
#include <stdexcept> // std::logic_error
#include <assert.h>
#undef ASSERT_SHOULD_NEVER_GET_HERE
#ifdef NDEBUG
# define ASSERT_SHOULD_NEVER_GET_HERE() /
throw std::logic_error( "Reached a supposed unreachable point" )
#else
# define ASSERT_SHOULD_NEVER_GET_HERE() /
do{ /
assert( "Reached a supposed unreachable point" && 0 ); /
throw 0; /
} while( 0 )
#endif
Descargo de responsabilidad: aunque lo codifiqué varias veces a principios de la década de 2000, preparé el código anterior solo para esta respuesta, y aunque lo probé con g ++, no necesariamente es perfecto.
(1) Consulte la respuesta de Basile Starynkevitch para conocer otra posibilidad, la intrínseca específica de g ++ __builtin_unreachable
.
Sí, siempre fallará.
assert(0)
o assert(false)
se usa generalmente para marcar código inalcanzable , de modo que en el modo de depuración se emite un mensaje de diagnóstico y el programa se cancela cuando se alcanza realmente el acceso supuestamente inalcanzable, que es una señal clara de que el programa no es t haciendo lo que pensamos que es.
Siempre fallará. Eso es practicamente todo. Siempre fallará por el mismo motivo que "assert (x == 5)" tendrá éxito siempre que x = 5.
Si está solicitando una aplicación, entonces la pondría en bloques de código que realmente no deberían suceder.
switch(suit) {
case CLUB:
case DIAMOND:
case HEART:
case SPADE:
// ...
default:
assert(0);
}
Además de otras respuestas (especialmente this ), si está usando un GCC reciente (o Clang ), podría considerar usar algo de GCC incorporado , notablemente __builtin_unreachable()
lugar de __builtin_unreachable()
assert(0)
.
Hay algunas diferencias: primero, assert
se puede desactivar con -DNDEBUG
. Y __builtin_unreachable
cambiará la forma en que el compilador está optimizando su código.
Por supuesto, algunos compiladores no saben acerca de __builtin_unreachable
.
Y también podría considerar llamar a alguna función [[noreturn]]
C ++ (en C ++ 11 o superior) - o __attribute__((noreturn))
para GCC , como abort()
Por cierto, assert(0)
no es exactamente como lanzar una excepción (ya que se pueden detectar excepciones)