2012-01-22 6 views
16

Forse mi sto arrugginendo (di recente ho scritto in Python).cosa c'è di sbagliato nel dichiarare una variabile all'interno delle sue condizioni?

Perché questo non viene compilato?

if ((int i=f()) == 0) 

senza il () intorno al int i=f() ottengo un altro, molto più ragionevole errore di i non è essere booleano. Ma è per questo che volevo le parentesi in primo luogo!

La mia ipotesi sarebbe che l'utilizzo delle parentesi lo trasformi in un'espressione e che le istruzioni di dichiarazione non siano consentite in un'espressione. È così? E se sì, è una delle stranezze della sintassi del C++?

A proposito, mi è stato effettivamente cercando di fare questo:

if ((Mymap::iterator it = m.find(name)) != m.end()) 
    return it->second; 
+1

Cosa c'è che non va? Tutto –

+5

@VJovic - Vago se hai ottenuto il punteggio della tua reputazione con risposte così elaborate e utili;) – davka

+0

No, vorrei diventare negativo;) Ma seriamente, qualsiasi standard di codifica normale proibisce un codice così oscuro. –

risposta

37

È possibile dichiarare una variabile nel if dichiarazione in C++, ma è limitato per essere utilizzato con l'inizializzazione diretta e ha bisogno per convertire in un valore booleano:

if (int i = f()) { ... } 

C++ non ha nulla che potrebbe essere descritto come "espressione di dichiarazione", cioè [sotto] espressioni che dichiarano una variabile.

In realtà, ho appena guardato la clausola standard ed entrambe le forme di inizializzazione sono supportati in base al 6,4 [stmt.select] paragrafo 1:

... 
condition: 
    expression 
    attribute-specifier-seqopt decl-specifier-seq declarator = initializer-clause 
    attribute-specifier-seqopt decl-specifier-seq declarator braced-init-list 
... 

Cioè, è anche possibile scrivere :

if (int i{f()}) { ... } 

Ovviamente, questo funziona solo in C++ 2011 perché C++ 2003 non ha l'inizializzazione del controvento.

+0

Non penso che il tuo ultimo esempio sia legale. Puoi solo 'if (int i {f()}) {/*...*/}' o 'if (int i = f()) {/*...*/}'. _braced-init-list_ deve includere le parentesi. (_braced-init-list_ è "{_initializer-list_, OPT}" o "{}"). –

+0

@CharlesBailey: sì, hai ragione: in qualche modo ho scambiato la "parentesi di parentesi" per indicare (...). Lo aggiusterò. –

+0

grazie, sembra ragionevole, e la risposta di @ Ilya di seguito fornisce alcune motivazioni – davka

20

C'è un problema con ambito.

Si consideri il seguente codice:

if ((int a = foo1()) || (int b = foo2())) 
{ 
    bar(b); 
} 

Is b dichiarata all'interno del blocco? Cosa succede se foo1() restituisce true?

+0

dimenticato di menzionare che non lo faccio Penso che questo sia un problema di scope come '()' AFAIK non crea scope. Mi sbaglio? – davka

+3

@SoapBox ma cosa succede se foo1 restituisce 1 e per valutazione di cortocircuito (che è garantito per quanto ne so) l'assegnazione di 'b' viene saltata? – lccarrasco

+0

Esatto - Non è possibile proporre di consentire la dichiarazione delle variabili in() a meno che non si risolva il problema dell'ambito; le variabili in C++ hanno scope che inizia quando la loro dichiarazione è 'eseguita', cosa che potrebbe non verificarsi mai in questa situazione. – greggo

4

È possibile dichiarare una variabile in un'istruzione if (o in for o while), ma solo nel blocco parentesi esterna e deve essere in grado di essere convertita in bool.

vostra congettura è fondamentalmente ragione, non è consentito perché

(int i = 42;) 

non è una dichiarazione valida con l'inizializzazione.

Hai bisogno di una linea aggiuntiva,

Mymap::iterator it; 
if ((it = m.find(name)) != m.end()) 
    return it->second; 

ma allora è meglio scrivere

Mymap::iterator it = m.find(name); 
if (it != m.end()) 
    return it->second; 

si può mettere la linea return dopo il if, se si vuole veramente questa linea di nuovo, almeno per me questo non danneggia la leggibilità, ma altri potrebbero vederlo diverso.

Se davvero, vuole veramente dichiarare un iteratore e utilizzare il come bool in una condizione if, si potrebbe fare

if (struct { int it; operator bool() { return it != m.end; } } s = { m.find(name) }) 
    return s.it->second; 

ma vorrei prendere in considerazione questo dannoso ;-)

0

è vero che non si può scrivere

if ((int i=f()) == 0) 

ma si può perfettamente scrivere

if (int i=f()) 

modo da poter utilizzare l'operatore && per eseguire entrambe le operazioni in una dichiarazione come

if (int i=1 && (i=f()) == 0) 

i deve essere inizializzato con un valore diverso da 0, e dovrebbe essere la prima condizione se il compilatore applica sinistra valutazione di destra.

Ma sfortunatamente, ciò non è applicabile nel caso di iteratori come richiede il secondo esempio.

Problemi correlati