C ++, statement de variables en la expresión ‘if’

¿Que está pasando aqui?

if(int a = Func1()) { // Works. } if((int a = Func1())) { // Fails to compile. } if((int a = Func1()) && (int b = Func2())) ) { // Do stuff with a and b. // This is what I'd really like to be able to do. } 

La sección 6.4.3 de la norma 2003 amplía cómo las variables declaradas en una condición de statement de selección tienen scope que se extiende hasta el final de las subestaciones controladas por la condición. Pero no veo dónde dice nada acerca de no poder poner paréntesis alrededor de la statement, ni dice nada acerca de una sola statement por condición.

Esta limitación es molesta incluso en los casos en que solo se requiere una statement en la condición. Considera esto.

 bool a = false, b = true; if(bool x = a || b) { } 

Si deseo ingresar el ámbito del cuerpo “si” con x establecido en falso, entonces la statement necesita paréntesis (ya que el operador de asignación tiene una precedencia menor que el OR lógico), pero como el paréntesis no se puede usar, requiere la statement de x afuera el cuerpo, filtrando esa statement a un scope mayor que el deseado. Obviamente, este ejemplo es trivial, pero un caso más realista sería aquel en que a y b son funciones que devuelven valores que deben ser probados

Entonces, ¿qué es lo que quiero hacer que no cumpla con el estándar o mi comstackdor está destruyendo mis bolas (VS2008)?

La condición en una instrucción if o while puede ser una expresión o una sola statement de variable (con inicialización).

Su segundo y tercer ejemplo no son expresiones válidas, ni declaraciones válidas, ya que una statement no puede formar parte de una expresión. Si bien sería útil poder escribir código como su tercer ejemplo, requeriría un cambio significativo en la syntax del lenguaje.

No veo dónde dice nada acerca de no poder poner paréntesis alrededor de la statement, ni dice nada acerca de una sola statement por condición.

La especificación de syntax en 6.4 / 1 da lo siguiente para la condición:

 condition: expression type-specifier-seq declarator = assignment-expression 

especificando una sola statement, sin paréntesis u otros adornos.

Creo que ya insinuó el problema. ¿Qué debería hacer el comstackdor con este código?

 if (!((1 == 0) && (bool a = false))) { // what is "a" initialized to? 

El operador “&&” es un cortocircuito lógico AND. Eso significa que si la primera parte (1==0) resulta ser falsa, entonces la segunda parte (bool a = false) no debe evaluarse porque ya se sabe que la respuesta final será falsa. Si (bool a = false) no se evalúa, ¿qué hacer con el código más adelante que utiliza a ? ¿No podríamos inicializar la variable y dejarla indefinida? ¿Lo inicializaríamos con el valor predeterminado? ¿Qué pasa si el tipo de datos es una clase y hacer esto tiene efectos secundarios indeseables? ¿Qué bool si en lugar de bool utiliza una clase y no tiene un constructor predeterminado para que el usuario debe proporcionar los parámetros? ¿Qué hacemos entonces?

Aquí hay otro ejemplo:

 class Test { public: // note that no default constructor is provided and user MUST // provide some value for parameter "p" Test(int p); } if (!((1 == 0) && (Test a = Test(5)))) { // now what do we do?! what is "a" set to? 

Parece que la limitación que ha encontrado parece perfectamente razonable: evita que ocurran este tipo de ambigüedades.

Si desea encerrar variables en un ámbito más estrecho, siempre puede usar { } adicional

 //just use { and } { bool a = false, b = true; if(bool x = a || b) { //... } }//a and b are out of scope 

A partir de C ++ 17, lo que intentabas hacer es finalmente posible :

 if (int a = Func1(), b = Func2(); a && b) { // Do stuff with a and b. } 

Tenga en cuenta el uso de ; de en lugar de , para separar la statement y la condición real.

La última sección ya funciona, solo tienes que escribirla ligeramente diferente:

 if (int a = Func1()) { if (int b = Func2()) { // do stuff with a and b } } 

Aquí hay una solución fea utilizando un bucle (si ambas variables son números enteros):

 #include  int func1() { return 4; } int func2() { return 23; } int main() { for (int a = func1(), b = func2(), i = 0; i == 0 && a && b; i++) { std::cout << "a = " << a << std::endl; std::cout << "b = " << b << std::endl; } return 0; } 

Pero esto confundirá a otros progtwigdores y es un código bastante malo, por lo que no es recomendable.

Un bloque {} encerramiento simple (como ya se recomendó) es mucho más fácil de leer:

 { int a = func1(); int b = func2(); if (a && b) { std::cout << "a = " << a << std::endl; std::cout << "b = " << b << std::endl; } } 

Una cosa a tener en cuenta, también es que las expresiones dentro del bloque if más grande

 if (!((1 == 0) && (bool a = false))) 

no necesariamente se garantiza que se evalúen de izquierda a derecha. Un error bastante sutil que tuve en el día tenía que ver con el hecho de que el comstackdor realmente estaba probando de derecha a izquierda en lugar de izquierda a derecha.

Con un poco de magia de plantilla, puede solucionar el problema de no poder declarar múltiples variables:

 #include  template  struct And_t { LHS lhs; RHS rhs; operator bool () { bool b_lhs(lhs); bool b_rhs(rhs); return b_lhs && b_rhs; } }; template  And_t And(const LHS& lhs, const RHS& rhs) { return {lhs, rhs}; } template  struct Or_t { LHS lhs; RHS rhs; operator bool () { bool b_lhs(lhs); bool b_rhs(rhs); return b_lhs || b_rhs; } }; template  Or_t Or(const LHS& lhs, const RHS& rhs) { return {lhs, rhs}; } int main() { if (auto i = And(1, Or(0, 3))) { printf("%d %d %d\n", i.lhs, i.rhs.lhs, i.rhs.rhs); } return 0; } 

(Nota, esto pierde la evaluación de cortocircuito.)