c++ - switch - Asignación variable en condición "si"
programas de c++ if (7)
¿Por qué el compilador no lanza una advertencia?
Algunos compiladores generarán advertencias para las asignaciones sospechosas en una expresión condicional, aunque generalmente debe habilitar la advertencia explícitamente.
Por ejemplo, en Visual C ++, debe habilitar C4706 (o advertencias de nivel 4 en general). En general, enciendo tantas advertencias como puedo y hago que el código sea más explícito para evitar falsos positivos. Por ejemplo, si realmente quisiera hacer esto:
if (x = Foo()) { ... }
Entonces lo escribiría como:
if ((x = Foo()) != 0) { ... }
El compilador ve la prueba explícita y asume que la asignación fue intencional, por lo que no recibirá una advertencia positiva falsa aquí.
El único inconveniente de este enfoque es que no puede usarlo cuando la variable se declara en la condición. Es decir, no puede reescribir:
if (int x = Foo()) { ... }
como
if ((int x = Foo()) != 0) { ... }
Sintácticamente, eso no funciona. Entonces, o tiene que deshabilitar la advertencia, o comprometerse en cuán estricto es el alcance x
.
Esta pregunta ya tiene una respuesta aquí:
- ¿Por qué usarías una tarea en una condición? 11 respuestas
Recientemente perdí algo de tiempo descubriendo un error en mi código que fue causado por un error tipográfico:
if(a=b)
en lugar de:
if(a==b)
Me preguntaba si hay algún caso particular en el que quiera asignar un valor a una variable en una declaración if
, o si no, ¿por qué el compilador no emite una advertencia o un error?
Aquí hay un poco de historia sobre la sintaxis en cuestión.
En la clásica C, el manejo de errores se hacía frecuentemente escribiendo algo como:
int error;
...
if(error = foo()) {
printf("An error occured: %s/nBailing out./n", strerror(error));
abort();
}
O bien, cada vez que había una llamada de función que podía devolver un puntero nulo, la expresión se usaba al revés:
Bar* myBar;
... //in old C variables had to be declared at the start of the scope
if(myBar = getBar()) {
//do something with myBar
}
Sin embargo, esta sintaxis es peligrosamente cercana a
if(myValue == bar()) ...
por eso, muchas personas consideran que la tarea dentro de un mal estado de condición, y los compiladores comenzaron a advertir al respecto (al menos con -Wall
). Sin embargo, esta advertencia se puede evitar agregando un conjunto adicional de paréntesis:
if((myBar = getBar())) { //tells the compiler: Yes, I really want to do that assignment!
Luego apareció C99, permitiéndote mezclar definiciones y enunciados, por lo que muchos desarrolladores escribían con frecuencia algo así como
Bar* myBar = getBar();
if(myBar) {
que se siente incomodo Esta es la razón por la cual el estándar más nuevo permite definiciones dentro de las condiciones, para proporcionar una manera corta y elegante de hacer esto:
if(Bar* myBar = getBar()) {
Ya no hay peligro en esta afirmación, explícitamente le das un tipo a la variable, obviamente, deseando que se inicialice. También evita la línea adicional para definir la variable, lo cual es bueno. Pero lo más importante, el compilador ahora puede detectar fácilmente este tipo de error:
if(Bar* myBar = getBar()) {
...
}
foo(myBar->baz); //compiler error
//or, for the C++ enthusiasts:
myBar->foo(); //compiler error
Sin la definición variable dentro de la declaración if
, esta condición no sería detectable.
Para abreviar una larga respuesta: la sintaxis en su pregunta es producto de la simplicidad y el poder del antiguo C, pero es malvada, por lo que los compiladores pueden advertir al respecto. Dado que también es una forma muy útil de expresar un problema común, ahora existe una forma muy concisa y resistente a errores para lograr el mismo comportamiento. Y hay muchos buenos usos posibles para ello.
Depende de si desea escribir código limpio o no. Cuando se desarrolló C por primera vez, la importancia del código limpio no se reconoció del todo, y los compiladores eran muy simplistas: el uso de asignaciones anidadas como esta a menudo podía dar como resultado un código más rápido. Hoy, no puedo pensar en ningún caso donde un buen programador lo haría. Simplemente hace que el código sea menos legible y más difícil de mantener.
El operador de asignación devuelve el valor del valor asignado . Entonces, podría usarlo en una situación como esta:
if(x = getMyNumber())
Luego asigno x
para que sea el valor devuelto por getMyNumber
y getMyNumber
si no es cero.
Evita hacer eso, te di un ejemplo solo para ayudarte a entender esto.
Editar: agregando solo una sugerencia (puede gustarle).
Para evitar estos errores hasta cierto punto, se debe escribir si la condición es como if(NULL == ptr)
lugar de if(ptr == NULL)
Porque cuando se escribe mal el operador de verificación de igualdad ==
como operador =
, la compilación arrojará un lvalue error con if(NULL = ptr)
, pero if(res = NULL)
pasó por el compilador (que no es lo que quiere decir) y sigue siendo un error en el código para el tiempo de ejecución.
Por la misma razón, preferiría escribir if(getMyNumber() ==x)
lugar de if(x == getMyNumber())
También se debe leer: Criticism respecto a este tipo de código.
Hacer una tarea en un if
es algo bastante común, aunque también es común que las personas lo hagan por accidente.
El patrón habitual es:
if (int x = expensive_function_call())
{
// ...do things with x
}
El antipatrón es donde estás asignando cosas por error:
if (x = 1)
{
// Always true
}
else
{
// Never happens
}
Puede evitar esto hasta cierto punto poniendo sus constantes o valores const
primero, por lo que su compilador arrojará un error:
if (1 = x)
{
// Compiler error, can''t assign to 1
}
=
vs. ==
es algo por lo que necesitarás desarrollar un ojo. Normalmente coloco espacios en blanco alrededor del operador, por lo que es más obvio qué operación se está realizando, ya que longname=longername
parece mucho a longname==longername
de un vistazo, pero =
y ==
por su propia cuenta son obviamente diferentes.
Me encontré con un caso en el que esto fue útil recientemente, así que pensé en publicarlo.
Supongamos que desea verificar varias condiciones en una sola si, y si alguna de las condiciones es verdadera, desea generar un mensaje de error. Si desea incluir en su mensaje de error qué condición específica causó el error, podría hacer lo siguiente:
std::string e;
if( myMap[e = "ab"].isNotValid() ||
myMap[e = "cd"].isNotValid() ||
myMap[e = "ef"].isNotValid() )
{
// here, e has the key for which the validation failed
}
Entonces, si la segunda condición es la que se evalúa como verdadera, e será igual a "cd". Esto se debe al comportamiento de cortocircuito de ||
que es obligatorio por el estándar (a menos que esté sobrecargado). Vea this respuesta para más detalles sobre el cortocircuito.
if (Derived* derived = dynamic_cast<Derived*>(base)) {
// do stuff with `derived`
}
Aunque esto se suele citar como un antipatrón ("uso de despacho virtual"), a veces el tipo Derived
tiene una funcionalidad que la Base
simplemente no tiene (y, en consecuencia, funciones distintas), y esta es una buena manera de activar esa función. diferencia semántica