C ++, declaração de variável na expressão ‘if’

Oque esta acontecendo 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. } 

A seção 6.4.3 do padrão de 2003 expõe como as variables ​​declaradas em uma condição de declaração de seleção têm escopo que se estende até o final das subestações controladas pela condição. Mas eu não vejo onde diz nada sobre não ser capaz de colocar parênteses em torno da declaração, nem diz nada sobre apenas uma declaração por condição.

Essa limitação é irritante mesmo nos casos em que apenas uma declaração na condição é necessária. Considere isto.

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

Se eu quiser entrar no escopo de corpo “if” com x definido como falso, a declaração precisa de parênteses (já que o operador de atribuição tem precedência menor que a OR lógica), mas como parênteses não podem ser usados, exige declaração de x o corpo, vazando essa declaração para um escopo maior do que o desejado.obviamente este exemplo é trivial, mas um caso mais realista seria aquele em que aeb são funções retornando valores que precisam ser testados

Então, é o que eu quero fazer em conformidade com o padrão, ou o meu compilador está apenas quebrando minhas bolas (VS2008)?

A condição em uma instrução if ou while pode ser uma expressão ou uma única declaração de variável (com boot).

Seu segundo e terceiro exemplos não são expressões válidas nem declarações válidas, já que uma declaração não pode fazer parte de uma expressão. Embora seja útil poder escrever código como seu terceiro exemplo, isso exigiria uma alteração significativa na syntax da linguagem.

Eu não vejo onde diz nada sobre não ser capaz de colocar parênteses em torno da declaração, nem diz nada sobre apenas uma declaração por condição.

A especificação de syntax em 6.4 / 1 fornece o seguinte para a condição:

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

especificando uma única declaração, sem parênteses ou outros adornos.

Eu acho que você já sugeriu a questão. O que o compilador deve fazer com esse código?

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

O operador “&&” é um AND lógico de curto-circuito. Isso significa que se a primeira parte (1==0) for falsa, a segunda parte (bool a = false) não deve ser avaliada porque já se sabe que a resposta final será falsa. Se (bool a = false) não é avaliado, então o que fazer com o código mais tarde que usa a ? Nós simplesmente não inicializamos a variável e a deixamos indefinida? Será que vamos inicializá-lo para o padrão? E se o tipo de dados fosse uma class e isso tivesse efeitos colaterais indesejáveis? E se, em vez de bool você usasse uma class e não tivesse um construtor padrão, de forma que o usuário deveria fornecer parâmetros – o que fazemos então?

Aqui está outro exemplo:

 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 a limitação que você encontrou parece perfeitamente razoável – impede que esse tipo de ambigüidade aconteça.

Se você quiser include variables ​​em um escopo mais restrito, use sempre { } 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, o que você estava tentando fazer é finalmente possível :

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

Observe o uso de ; de, em vez de , separar a declaração e a condição atual.

A última seção já funciona, você só precisa escrever um pouco diferente:

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

Aqui está uma solução feia usando um loop (se as duas variables ​​forem inteiras):

 #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; } 

Mas isso vai confundir outros programadores e é um código ruim, então não é recomendado.

Um simples bloco {} delimitador (como já recomendado) é muito mais fácil de ler:

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

Uma coisa a notar, também é que as expressões dentro do bloco if maior

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

não é necessariamente garantido para ser avaliado de forma da esquerda para a direita. Um bug bastante sutil que eu tive no passado tinha a ver com o fato de que o compilador estava realmente testando da direita para a esquerda em vez de da esquerda para a direita.

Com um pequeno modelo de magia você pode meio que contornar o problema de não poder declarar múltiplas 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; } 

(Observe que isso perde a avaliação de curto-circuito.)