2012-01-22 11 views
16

Być może jestem zardzewiały (pisałem ostatnio w Pythonie).co jest złego w deklarowaniu zmiennej w warstwie if?

Dlaczego to się nie kompiluje?

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

bez () wokół int i=f() I dostać inny, o wiele bardziej rozsądne błąd i nie jest logiczna. Ale właśnie dlatego chciałem nawiasów w pierwszej kolejności!

Domyślam się, że użycie nawiasów czyni go wyrażeniem, a deklaracje deklaracji nie są dozwolone w wyrażeniu. Czy to prawda? A jeśli tak, czy jest to jeden z dziwactw składniowych C++?

BTW, ja rzeczywiście próbuje to zrobić:

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

Co jest nie tak? Wszystko –

+5

@VJovic - Błądzę, jeśli uzyskałeś swoją reputację dzięki tak wyszukanym, pomocnym odpowiedziom;) – davka

+0

Nie, chciałbym pójść negatywnie;) Ale poważnie, normalne standardy kodowania zabraniają takiego niejasnego kodu. –

Odpowiedz

37

Można zadeklarować zmienną w rachunku w C if ++, ale jest ograniczony do stosowania z bezpośrednim inicjalizacji i potrzebuje do konwersji na wartość logiczną:

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

C++ nie ma nic co mogłoby być opisane jako "wyrażenie deklaracji", tj. [pod-] wyrażenia deklarujące zmienną.

Właściwie, po prostu spojrzał na klauzulę w normie i obie formy inicjalizacji są obsługiwane zgodnie z 6.4 [stmt.select] ustęp 1:

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

Oznacza to, że jest to również możliwe, aby napisać :

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

Oczywiście działa to tylko w C++ 2011, ponieważ C++ 2003 nie ma inicjalizacji nawiasów klamrowych.

+0

Nie sądzę, że twój ostatni przykład jest legalny. Możesz zrobić tylko 'if (int i {f()}) {/*...*/}' lub 'if (int i = f()) {/*...*/}'. _braced-init-list_ musi zawierać nawiasy klamrowe. (_braced-init-list_ to "{_initializer-list_, OPT}" lub "{}"). –

+0

@CharlesBailey: tak, masz rację: jakoś pomyliłem "usztywnioną listę-inicjacji", żeby to powiedzieć (...). Naprawię to. –

+0

dziękuję, brzmi rozsądnie, a odpowiedź @llya poniżej daje pewne uzasadnienie – davka

20

Jest problem z zakresu.

Rozważmy następujący kod:

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

Czy b deklarowane wewnątrz bloku? Co jeśli foo1() zwróci true?

+0

zapomniałem wspomnieć, że nie jestem " t myśli, że jest to kwestia zakresu, ponieważ '()' AFAIK nie tworzy zakresu. Czy się mylę? – davka

+3

@SoapBox, ale co się stanie, jeśli foo1 zwróci 1, a ocena zwarcia (która jest gwarantowana z tego co wiem) przypisanie 'b' jest pomijane? – lccarrasco

+0

Dokładnie - Nie możesz zaproponować dopuszczenia zmiennych do deklarowania w(), chyba że zajmiesz się problemem zakresu; zmienne w C++ mają zasięg, który zaczyna się, gdy ich deklaracja jest "wykonywana", co może nigdy nie wystąpić w tej sytuacji. – greggo

4

Możesz zadeklarować zmienną w instrukcji if (lub in lub for), ale tylko w zewnętrznym bloku nawiasów i musi istnieć możliwość przekonwertowania na wartość bool.

Twoje przypuszczenie jest w zasadzie w porządku, to nie wolno bo

(int i = 42;) 

nie jest nieważny z inicjalizacji.

Musisz jedną dodatkową linię

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

ale to lepiej napisać

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

można umieścić linię return po if, jeśli naprawdę chcesz tę linię do tyłu, co najmniej dla mnie nie szkodzi to czytelności, ale inni mogą zobaczyć to inaczej.

Jeśli naprawdę chcą zadeklarować iterator i używać go jako bool w stanie if, można zrobić

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

ale uważam to szkodliwe ;-)

0

To prawda że nie można napisać

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

ale można doskonale pisać

if (int i=f()) 

Więc można użyć operatora && wykonywać zarówno operacje w jednej instrukcji jak

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

i powinny być inicjowane z dowolną wartość inną niż 0, a powinien to być pierwszy warunek, jeśli kompilator stosuje lewo prawostronna ocena.

Niestety, nie ma to zastosowania w przypadku iteratorów, o co pyta się w drugim przykładzie.