2011-05-09 6 views
15

bezmyślnie pisałem jakiś kod, aby sprawdzić, czy wszystkie wartości struct były ustawione na 0. Aby to osiągnąć użyłem:wartości Chaining Bool dać skutek odwrotny do oczekiwanego

bool IsValid() { 
    return !(0 == year == month == day == hour == minute == second); 
} 

gdzie wszyscy członkowie struct były typu Niepodpisany krótki. Użyłem kodu jako części większego testu, ale zauważyłem, że zwracano wartość false w przypadku wartości różniących się od zera, i prawdziwe w przypadku wartości, które były wszystkie równe zeru - przeciwieństwo tego, czego się spodziewałem.

Zmieniłem kod do odczytu:

bool IsValid() { 
    return (0 != year) || (0 != month) || (0 != day) || (0 != hour) || (0 != minute) || (0 != second); 
} 

Ale chciałbym wiedzieć, co spowodowało dziwne zachowanie. Czy to wynik precedensu? Próbowałem użyć tej odpowiedzi, ale nic nie znalazłem, jeśli jest jakaś nomenklatura do opisania wyniku, który chciałbym poznać.

Skompilowałem kod przy użyciu VS9 i VS8.

+0

Nieświadomie natknąłeś się na integralne zasady promocji. Bool może być promowany na liczbę całkowitą, więc test 'bool == int' jest ważny ... nawet jeśli jest wyjątkowo sprzeczny z intuicją. Ach! –

+0

Jako notatkę "a == b == c == d == 0" działałoby na Pythonie. – liori

Odpowiedz

26

== grupy od lewej do prawej strony, więc jeśli wszystkie wartości są równe zeru wtedy:

0 == year // true 
(0 == year) == month // false, since month is 0 and (0 == year) converts to 1 
((0 == year) == month) == day // true 

I tak dalej.

Ogólnie rzecz biorąc, x == y == z to nie równoważne x == y && x == z, jak można się spodziewać.

+0

To jest moja ulubiona odpowiedź w odniesieniu do prostoty. Charles Bailey dokładnie to zakrył, ale twoja odpowiedź przynosi bardzo cenną klarowność. Dziwne jest sądzić, że założę się, że kompilator * wie * co mam na myśli. – 17Twenty

15

Zachowanie nie powinno być postrzegane jako nieparzyste. Reguły gramatyczne dla == (a większość ale nie wszystkie operatory binarne) określa lewej do prawej grupowania tak oryginalny wyrażenie jest równoważne:

!((((((0 == year) == month) == day) == hour) == minute) == second) 

Należy zauważyć, że w porównaniu do liczby całkowitej wpisać bool wyrażenie z wartością true będzie promować do 1 i o wartości false będzie promować do 0. (W C wynikiem operatora równości jest int w każdym przypadku o wartości lub też 1 lub 0).

Oznacza to, że, na przykład, ((0 == year) == month) będzie ważne, jeśli year oznacza zero month jeden lub year jest niezerowe, ale month ma wartość zero, a w przeciwnym razie false.

8

Trzeba zastanowić się, jak to oceniać ...

a == b == c 

pyta, czy dwa z nich są równe (a i b), a następnie porównując to logiczna wynik do wartości trzeciego c! NIE porównuje dwóch pierwszych wartości z trzecim. Cokolwiek poza 2 argumentami nie będzie łańcucha, jak najwyraźniej się spodziewasz.

Na co warto, ponieważ C++ uważa non-0 Wartości być „true” w kontekście logicznym, można wyrazić to, co chcesz po prostu jako:

return year && month && day && hour && minute && second; 

(UWAGA: Zmieniony kodeks mówi " miesiąc "dwa razy i nie testuje minuty).

Powrót do przykuty == s: o rodzajach i operatora przeciążenia cię może utworzyć klasę, która porównuje jak można oczekiwać (i to może nawet pozwolić rzeczy jak 0 <= x < 10 do „pracy” w sposób Przeczytajmy w zdefiniowanych przez użytkownika matematyka), ale tworzenie czegoś specjalnego po prostu pomyli innych programistów, którzy już znają (dziwny) sposób, w jaki te rzeczy działają dla typów wbudowanych w C++. Warto zrobić jako programowanie trwające dziesięć/dwadzieścia minut, ale jeśli chcesz nauczyć się C++ dogłębnie (podpowiedź: potrzebujesz operatory porównania, aby zwrócić obiekt proxy, który pamięta, jaka będzie wartość po lewej stronie dla następnego porównania operator).

Wreszcie, czasami te "dziwne" wyrażeń boolowskich są użyteczne: na przykład a == b == (c == d) może być sformułowane w języku angielskim jako "albo (a == b) i (c == d), LUB (a! = B) i (c! = d) ", a może" równoważność a i b jest taka sama jak równoważność cid (czy prawda czy fałsz nie mają znaczenia) ". To może wzorować na rzeczywistych sytuacjach, takich jak scenariusz z podwójnymi randkami: jeśli lubi/nie lubi b (ich data) tyle, ile c lubi/nie lubi d, wtedy albo się rozejdzie i miło spędzą czas, albo zadzwonią szybko jest bezbolesny tak czy inaczej ... w przeciwnym razie jedna para będzie miała bardzo nudny czas ... Ponieważ te rzeczy mogą mieć sens, kompilator nie może wiedzieć, że nie zamierzałeś stworzyć takiego wyrażenia.

+0

Dobrze zauważony - m błąd z mojej strony. Napisałem C++ od dłuższego czasu, a ja i starszy programistka obaj się na to natknęliśmy - choć kiedy jest to uzasadnione, tak jak ty i inni, staje się to oczywiste. Rzucamy kłaczek na nasz kod i byliśmy zaskoczeni, że go nie podniosło i że kompilator nie * wie, co miałem na myśli - ostatecznie to moja porażka, i cieszę się, że mogę się z tego nauczyć. Dzięki. – 17Twenty

+0

@ 17Twenty: Na początku zaskakuje wszystkich - wygląda jak matematyka, ale zachowuje się zupełnie inaczej. Zastanawiam się, które języki robią to inaczej (może Miranda - od 20 lat używam tego). Dodałem notatkę do ostatniego akapitu o tym, dlaczego lint/kompilator itp. Nie może zdiagnozować tego bez ryzyka fałszywych alarmów. Twoje zdrowie. –

+0

Interesujący przykład, który może być fałszywie pozytywny, ale w C++ twój przykład porównuje dwa 'bool', podczas gdy kod pytającego porównuje' bool' do 'short'. Tak więc kompilator C++ może ostrzegać o jednym, ale nie o drugim, chociaż mogą istnieć inne przykłady, które mogłyby go wyzwolić. W C oba porównywają "właściwe" typy całkowite. –

1

Powrót operatora == to 1, jeśli operandy są równe, więc niezależnie od tego, czy jest to odczytane od lewej do prawej, czy od prawej do lewej, nie zrobi to, czego oczekujesz.

, więc może to działać tylko w analogicznym teście, jeśli byłbyś zainteresowany, gdyby wszystkie wartości były 1.

I mieć krótszą wyrażenie ponieważ wydaje się zainteresowany w tym po prostu zrobić year || day || ...

1

swój błąd tutaj pisze wyrażenie matematyczne użyciu równa znaki, i bezmyślnie zakładając, że komputer wykona test, który oznaczało - co ludzki matematyk zobaczyłby jako znaczenie tych symboli. To, co robi komputer (zgodnie z definicją języka), polega na przeprowadzeniu serii dyskretnych porównań, z których każdy zwraca true lub false - i to jest następnie używane w następnym porównaniu. Nie porównujesz wszystkich tych zmiennych do 0, porównujesz każde z nich (wynik z drugiego taktu) z wynikiem , porównując kolejne dwie z wymienionych zmiennych.

Powiązane problemy