switch - Declarar e inicializar una variable en una declaración condicional o de control en C++
switch c++ (9)
En The C ++ Programming Language: Special Edition (3rd Ed) de Stroustrup, Stroustrup escribe que la declaración y la inicialización de variables en los condicionales de las declaraciones de control no solo se permite, sino que se recomienda. Escribe que lo alienta porque reduce el alcance de las variables solo al alcance para el que son requeridas. Así que algo como esto ...
if ((int i = read(socket)) < 0) {
// handle error
}
else if (i > 0) {
// handle input
}
else {
return true;
}
... es un buen estilo de programación y práctica. La variable i
solo existe para el bloque de sentencias if
para las que se necesita y luego queda fuera del alcance.
Sin embargo, esta característica del lenguaje de programación no parece ser compatible con g ++ (compilación específica de Ubuntu para la versión 4.3.3), lo que me sorprende. Tal vez solo esté llamando a g ++ con una bandera que la apaga (las que he llamado son -g
y -Wall
). Mi versión de g ++ devuelve el siguiente error de compilación al compilar con esos indicadores:
socket.cpp:130: error: expected primary-expression before ‘int’
socket.cpp:130: error: expected `)'' before ‘int’
En una investigación adicional descubrí que no parecía ser el único con un compilador que no es compatible con esto. Y parecía haber cierta confusión en esta pregunta en cuanto a qué exactamente la sintaxis era supuestamente estándar en el lenguaje y qué compiladores compilaban con ella.
Entonces, la pregunta es: ¿qué compiladores admiten esta característica y qué indicadores deben configurarse para que compile? ¿Es una cuestión de estar en ciertos estándares y no en otros?
Además, solo por curiosidad, ¿la gente generalmente está de acuerdo con Stroustrup en que este es un buen estilo? ¿O es esta una situación donde el creador de un lenguaje tiene una idea en su cabeza que no es necesariamente apoyada por la comunidad del lenguaje?
Agregando a lo que dijeron RedGlyph y Ferruccio. Puede ser que podamos hacer lo siguiente para declarar dentro de una declaración condicional para limitar su uso:
if(int x = read(socket)) //x != 0
{
if(x < 0) //handle error
{}
else //do work
{}
}
else //x == 0
{
return true;
}
Considero que es un buen estilo cuando se usa con un puntero NULL posiblemente:
if(CObj* p = GetOptionalValue()) {
//Do something with p
}
De esta manera si se declara p, es un puntero válido. No hay puntero colgando peligro de acceso.
Por otro lado, al menos en VC ++ es el único uso compatible (es decir, verificar si la asignación es verdadera)
Ellos están arreglando esto en c ++ 17:
if (int i = read(socket); i < 0)
donde if
puede tener una sentencia inicializadora.
vea http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0305r0.html
Me he encontrado con un problem similar:
El problema parece ser el paréntesis alrededor de la declaración int
. Debería funcionar si puede expresar la tarea y la prueba sin ellos, es decir,
if (int i = read(socket)) {
debería funcionar, pero eso significa que la prueba es != 0
, que no es lo que quieres.
Para complementar las buenas respuestas de las otras personas, siempre puede limitar el alcance de la variable con llaves:
{
const int readResult = read(socket);
if(readResult < 0) {
// handle error
}
else if(readResult > 0)
{
// handle input
}
else {
return true;
}
}
Se permite declarar una variable en la parte de control de un bloque anidado, pero en el caso de if
y while
, la variable debe inicializarse a un valor numérico o booleano que se interpretará como la condición. ¡No se puede incluir en una expresión más compleja!
En el caso particular que muestra, no parece que pueda encontrar una manera de cumplir desafortunadamente.
Personalmente, creo que es una buena práctica mantener las variables locales lo más cerca posible de su vida útil real en el código, incluso si suena impactante cuando cambia de C a C ++ o de Pascal a C ++. Estábamos acostumbrados a ver todas las variables en un lugar. Con algún hábito, lo encuentras más legible y no tienes que buscar en otra parte para encontrar la declaración. Además, sabes que no se usa antes de ese punto.
Editar:
Dicho esto, no me parece una buena práctica mezclar demasiado en una sola declaración, y creo que es una opinión compartida. Si afecta un valor a una variable, luego utilícelo en otra expresión, el código será más legible y menos confuso al separar ambas partes.
Así que en lugar de usar esto:
int i;
if((i = read(socket)) < 0) {
// handle error
}
else if(i > 0) {
// handle input
}
else {
return true;
}
Preferiría que:
int i = read(socket);
if(i < 0) {
// handle error
}
else if(i > 0) {
// handle input
}
else {
return true;
}
Si bien no está directamente relacionado con la pregunta, todos los ejemplos ponen el manejo de errores primero. Como hay 3 casos (> 0 -> datos, == 0 -> conexión cerrada y <0 -> error), eso significa que el caso más común de obtener datos nuevos requiere dos pruebas. La comprobación de> 0 primero reduciría el número esperado de pruebas en casi la mitad. Desafortunadamente, el enfoque "if (int x = read (socket))" dado por White_Pawn aún requiere 2 pruebas para el caso de los datos, pero la propuesta de C ++ 17 se podría usar para probar primero> 0.
Si bien puede usar una declaración como una expresión booleana, no puede colocar una declaración en medio de una expresión. No puedo evitar pensar que estás leyendo mal lo que dice Bjarne.
La técnica es útil y deseable principalmente para las variables de control de los bucles for, pero en este caso creo que no es aconsejable y no sirve para aclarar. Y por supuesto que no funciona! ;)
if( <type> <identifier> = <initialiser> ) // valid, but not that useful IMO
if( (<type> <identifier> = <initialiser>) <operator> <operand> ) // not valid
for( <type> <identifier> = <initialiser>;
<expression>;
<expression> ) // valid and desirable
En su ejemplo, ha llamado a una función con efectos secundarios en un condicional, cuya OMI es una mala idea, independientemente de lo que pueda pensar acerca de declarar la variable allí.
Uso const en la mayor medida posible en estas situaciones. En lugar de tu ejemplo, yo haría:
const int readResult = read(socket);
if(readResult < 0) {
// handle error
}
else if(readResult > 0)
{
// handle input
}
else {
return true;
}
Entonces, aunque el alcance no está contenido, en realidad no importa, ya que la variable no puede modificarse.