tutorial switch program example con code blocks c++ switch-statement compiler-warnings

program - switch syntax c++



C++ Fuerza el error/advertencia en tiempo de compilación en la caída implícita en el conmutador (4)

Aquí hay una respuesta al odio compulsivo.

Primero, las declaraciones de switch son fantasías fantasiosas. Se pueden combinar con otro flujo de control (famoso, el dispositivo de Duff ), pero la analogía obvia aquí es un goto o dos. Aquí hay un ejemplo inútil:

switch (var) { CASE1: case 1: if (foo) goto END; //same as break goto CASE2; //same as fallthrough CASE2: case 2: break; CASE3: case 3: goto CASE2; //fall *up* CASE4: case 4: return; //no break, but also no fallthrough! DEFAULT: default: continue; //similar, if you''re in a loop } END:

¿Recomiendo esto? No. De hecho, si está considerando esto solo para anotar una falla, entonces su problema es en realidad otra cosa.

Este tipo de código deja muy en claro que puede ocurrir una falla en el caso 1, pero como muestran los otros bits, esta es una técnica muy poderosa en general que también se presta al abuso. Ten cuidado si lo usas.

¿Olvidando un break ? Bueno, entonces ocasionalmente también olvidarás cualquier anotación que elijas. ¿Olvidar tener en cuenta la falla al cambiar una declaración de cambio? Eres un mal programador. Al modificar las declaraciones de cambio (o realmente cualquier código) , primero debe comprenderlas .

Honestamente, rara vez cometo este tipo de error (olvidando un descanso), ciertamente menos que otros errores de programación "comunes" (alias estricto, por ejemplo). Para estar seguro, actualmente lo hago (y recomiendo que lo hagas) simplemente escribo //fallthrough , ya que esto al menos aclara la intención.

Aparte de eso, es una realidad que los programadores deben aceptar. Revise su código después de escribirlo y encuentre el problema ocasional con la depuración. Así es la vida.

switch declaraciones de switch pueden ser súper útiles, pero conducen a un error común en el que un programador olvidó una declaración de interrupción:

switch(val) { case 0: foo(); break; case 1: bar(); // oops case 2: baz(); break; default: roomba(); }

Obviamente, no recibirá una advertencia, ya que a veces se desea explícitamente la falla. Un buen estilo de codificación sugiere comentar cuando su caída es deliberada, pero a veces eso es insuficiente.

Estoy bastante seguro de que la respuesta a esta pregunta es no, pero: ¿hay alguna forma actualmente (o propuesta en el futuro) para poder pedirle al compilador que arroje un error (o al menos una advertencia!) Si su case hace no tener al menos uno de break; o algo en el sentido de // fallthru ? Sería bueno tener una opción de programación defensiva para usar declaraciones de switch .


Bueno, clang tiene -Wimplicit-fallthrough que no conocía pero encontré usando -Weverything . Entonces, para este código, me da la siguiente advertencia ( verlo en vivo ):

warning: unannotated fall-through between switch labels [-Wimplicit-fallthrough] case 2: ^ note: insert ''[[clang::fallthrough]];'' to silence this warning case 2: ^ [[clang::fallthrough]]; note: insert ''break;'' to avoid fall-through case 2: ^ break;

La única documentación que puedo encontrar para este indicador está en la Referencia de atributos que dice:

El atributo clang :: fallthrough se usa junto con el argumento -Wimplicit-fallthrough para anotar la caída intencional entre etiquetas de conmutador. Solo se puede aplicar a una declaración nula colocada en un punto de ejecución entre cualquier declaración y la siguiente etiqueta de cambio. Es común marcar estos lugares con un comentario específico, pero este atributo está destinado a reemplazar los comentarios con una anotación más estricta, que el compilador puede verificar.

y proporciona un ejemplo de cómo marcar una caída explícita:

case 44: // warning: unannotated fall-through g(); [[clang::fallthrough]]; case 55: // no warning

Este uso de un attribute para marcar fallos explícitos tiene la desventaja de no ser portátil. Visual Studio genera un error y gcc genera la siguiente advertencia:

warning: attributes at the beginning of statement are ignored [-Wattributes]

lo cual es un problema si quieres usar -Werror .

Intenté esto con gcc 4.9 y parece que gcc no admite esta advertencia:

error: opción de línea de comando no reconocida ''-Wimplicit-fallthrough''

A partir de GCC 7 , se -Wimplicit-fallthrough y __attribute__((fallthrough)) se puede utilizar para suprimir las advertencias cuando el fallthrough es intencional. GCC reconoce los comentarios "fallidos" en ciertos escenarios, pero puede confundirse con bastante facilidad .

No veo una forma de generar tal advertencia para Visual Studio .

Tenga en cuenta que Chandler Carruth explica que -Weverything no es para uso en producción:

Este es un grupo de locos que literalmente habilita todas las advertencias en Clang. No uses esto en tu código. Está destinado estrictamente a los desarrolladores de Clang o para explorar qué advertencias existen.

pero es útil para descubrir qué advertencias existen.

C ++ 17 cambios

En C ++ 17 obtenemos el atributo [[fallthrough]] cubierto en [dcl.attr.fallthrough]p1 :

El error de token de atributo se puede aplicar a una declaración nula (9.2); tal declaración es una declaración fallida. El fallo de token de atributo aparecerá como máximo una vez en cada lista de atributos y no deberá estar presente ninguna cláusula de argumento de atributo. Una declaración fallida solo puede aparecer dentro de una declaración de interruptor adjunta (9.4.2). La siguiente declaración que se ejecute después de una declaración de caída será una declaración etiquetada cuya etiqueta es una etiqueta de caso o etiqueta predeterminada para la misma declaración de cambio. El programa está mal formado si no hay tal declaración.

...

[ Example: void f(int n) { void g(), h(), i(); switch (n) { case 1: case 2: g(); [[fallthrough]]; case 3: // warning on fallthrough discouraged h(); case 4: // implementation may warn on fallthrough i(); [[fallthrough]]; // ill-formed } } —end example ]

Ver ejemplo en vivo usando el atributo .


Consejo: si constantemente coloca una línea en blanco entre las cláusulas del caso, la ausencia de un ''salto'' se vuelve más visible para un humano que descrema el código:

switch (val) { case 0: foo(); break; case 1: bar(); case 2: baz(); break; default: roomba(); }

Esto no es tan efectivo cuando hay mucho código dentro de las cláusulas de casos individuales, pero eso tiende a ser un mal olor de código en sí mismo.


Siempre escribo un break; antes de cada case , como sigue:

switch(val) { break; case 0: foo(); break; case 1: bar(); break; case 2: baz(); break; default: roomba(); }

De esta manera, es mucho más obvio a la vista si hay un break; Está perdido. El break; inicial break; Supongo que es redundante, pero ayuda a ser coherente.

Esta es una declaración de switch convencional, simplemente he usado espacios en blanco de una manera diferente, eliminando la nueva línea que normalmente es después de un break; y antes del próximo case .