Czy jest jakiś powód, aby nie używać operatorów bitowych &, | i ^ dla wartości „bool” w C ++?
Czasami napotykam sytuacje, w których chcę, aby dokładnie jeden z dwóch warunków był prawdziwy (XOR), więc po prostu wrzucam operator ^ do wyrażenia warunkowego. Czasami chcę również, aby wszystkie części warunku były oceniane, czy wynik jest prawdziwy, czy nie (zamiast tworzyć zwarcie), więc używam & i |. Muszę też czasami gromadzić wartości logiczne, a & = i | = mogą być całkiem przydatne.
Robiąc to, dostałem kilka uniesionych brwi, ale kod jest nadal znaczący i czystszy niż byłby w innym przypadku. Czy jest jakiś powód, aby NIE używać ich do bools? Czy są jakieś nowoczesne kompilatory, które dają złe wyniki?
c++
boolean
bitwise-operators
Jay Conrod
źródło
źródło
Odpowiedzi:
||
i&&
są operatorami logicznymi, a wbudowane mają gwarancję zwrócenia albotrue
lubfalse
. Nic więcej.|
,&
I^
są bitowe operatorów. Kiedy domena liczb, na których operujesz, wynosi tylko 1 i 0, to są one dokładnie takie same, ale w przypadkach, gdy twoje wartości logiczne nie są ściśle 1 i 0 - jak w przypadku języka C - możesz skończyć z pewnym zachowaniem nie chciałeś. Na przykład:BOOL two = 2; BOOL one = 1; BOOL and = two & one; //and = 0 BOOL cand = two && one; //cand = 1
Jednak w C ++
bool
typ jest gwarantowany tylko jako atrue
lub afalse
(które konwertują niejawnie na odpowiednio1
i0
), więc takie podejście jest mniejszym zmartwieniem, ale fakt, że ludzie nie są przyzwyczajeni do oglądania takich rzeczy w kodzie to dobry argument, żeby tego nie robić. Po prostu powiedzb = b && x
i skończ z tym.źródło
Z dwóch głównych powodów. Krótko mówiąc, zastanów się dokładnie; może być dobry powód, ale jeśli w twoich komentarzach jest BARDZO wyraźny, ponieważ może być kruchy i, jak sam mówisz, ludzie nie są przyzwyczajeni do takiego kodu.
Bitwise xor! = Logical xor (z wyjątkiem 0 i 1)
Po pierwsze, jeśli operujesz na wartościach innych niż
false
itrue
(lub0
i1
jako liczby całkowite),^
operator może wprowadzić zachowanie, które nie jest równoważne z logicznym xor. Na przykład:int one = 1; int two = 2; // bitwise xor if (one ^ two) { // executes because expression = 3 and any non-zero integer evaluates to true } // logical xor; more correctly would be coded as // if (bool(one) != bool(two)) // but spelled out to be explicit in the context of the problem if ((one && !two) || (!one && two)) { // does not execute b/c expression = ((true && false) || (false && true)) // which evaluates to false }
Podziękowania dla użytkownika @Patrick za wyrażenie tego jako pierwszego.
Kolejność operacji
Po drugie
|
,&
i^
, jak operatorzy bitowe, nie zrobić zwarcia. Ponadto wiele operatorów bitowych połączonych ze sobą w jednej instrukcji - nawet z jawnymi nawiasami - można zmienić w kolejności poprzez optymalizację kompilatorów, ponieważ wszystkie 3 operacje są zwykle przemienne. Jest to ważne, jeśli liczy się kolejność operacji.Innymi słowy
bool result = true; result = result && a() && b(); // will not call a() if result false, will not call b() if result or a() false
nie zawsze da ten sam wynik (lub stan końcowy) jak
bool result = true; result &= (a() & b()); // a() and b() both will be called, but not necessarily in that order in an // optimizing compiler
Jest to szczególnie ważne, ponieważ możesz nie kontrolować metod
a()
ib()
, lub ktoś inny może przyjść i zmienić je później, nie rozumiejąc zależności, i spowodować nieprzyjemny (i często tylko do wydania) błąd.źródło
Myślę
jest tym, czego chcesz
źródło
!==
tak powiem), więc jeśli obliczasz XOR sekwencjibool
wartości, musisz zapisaćacc = acc!= condition(i);
w treści pętli. Kompilator prawdopodobnie poradzi sobie z tym tak wydajnie, jakby!==
istniał, ale niektórzy mogą uznać, że nie wygląda to ładnie i wolą alternatywę polegającą na dodaniu wartości logicznych jako liczb całkowitych, a następnie przetestowaniu, czy suma jest nieparzysta.Uniesione brwi powinny powiedzieć ci wystarczająco dużo, aby przestać to robić. Nie piszesz kodu dla kompilatora, piszesz go najpierw dla innych programistów, a potem dla kompilatora. Nawet jeśli kompilatory działają, zaskakiwanie innych ludzi nie jest tym, czego chcesz - operatory bitowe służą do operacji bitowych, a nie do bools.
Przypuszczam, że jesz też jabłka widelcem? Działa, ale zaskakuje ludzi, więc lepiej tego nie robić.
źródło
Wady operatorów poziomów bitowych.
Ty pytasz:
Tak, operatory logiczne , że jest wbudowany w wysokiego szczebla operatorów logicznych
!
,&&
a||
oferują następujące zalety:Gwarantowana konwersji argumentów do
bool
, czyli0
i1
wartości porządkowej.Gwarantowana ocena zwarcia, w której ocena wyrażenia zatrzymuje się, gdy tylko znany jest wynik końcowy.
Można to zinterpretować jako logikę wartości drzewa, z True , False i Indeterminate .
Czytelne odpowiedniki tekstowe
not
,and
aor
nawet jeśli nie będę z nich korzystać siebie.Jako czytelnik antymonu zauważa w komentarzu też bitlevel operatorzy mają alternatywne znaki, a mianowicie
bitand
,bitor
,xor
icompl
, ale moim zdaniem są mniej czytelne niżand
,or
inot
.Mówiąc najprościej, każda taka zaleta operatorów wysokiego poziomu jest wadą operatorów bitlevel.
W szczególności, ponieważ operatory bitowe nie mają konwersji argumentów na 0/1, otrzymasz np.
1 & 2
→0
, while1 && 2
→true
. Ponadto^
bitowe wyłączne lub, mogą działać nieprawidłowo w ten sposób. Uważane za wartości logiczne 1 i 2 są takie same, mianowicietrue
, ale traktowane jako wzorce bitów są różne.Jak wyrazić logikę albo / lub w C ++.
Następnie podajesz trochę tła dla pytania,
Cóż, operatory bitowe mają wyższy priorytet niż operatory logiczne. Oznacza to w szczególności, że w wyrażeniu mieszanym, takim jak
otrzymujesz być może nieoczekiwany wynik
a && (b ^ c)
.Zamiast tego pisz po prostu
wyrażając bardziej zwięźle, co masz na myśli.
Dla wielu argumentów albo / albo nie ma operatora C ++, który wykonuje to zadanie. Na przykład, jeśli napiszesz
a ^ b ^ c
, to nie jest to wyrażenie, które mówi „alboa
,b
alboc
jest prawdziwe”. Zamiast tego mówi, „nieparzysta liczbaa
,b
ic
są prawdziwe”, co może być 1 z nich lub wszystkich 3 ...Aby wyrazić ogólne „albo / lub kiedy”
a
,b
ic
są typubool
, po prostu napisz(a + b + c) == 1
lub, jeśli nie są
bool
argumentami, zamień je nabool
:(!!a + !!b + !!c) == 1
Używanie
&=
do gromadzenia wyników boolowskich.Ty dalej rozwijasz,
Dobrze, odpowiada to sprawdzenie, czy odpowiednio wszystkie lub którykolwiek warunek jest spełniony, a de prawem Morgan mówi, jak przejść z jednego do drugiego. Oznacza to, że potrzebujesz tylko jednego z nich. Zasadniczo można by użyć
*=
jako&&=
operatora (jak odkrył stary dobry George Boole, logiczne ORAZ można bardzo łatwo wyrazić jako mnożenie), ale myślę, że to zdziwiłoby i być może wprowadziłoby w błąd opiekunów kodu.Weź również pod uwagę:
struct Bool { bool value; void operator&=( bool const v ) { value = value && v; } operator bool() const { return value; } }; #include <iostream> int main() { using namespace std; Bool a = {true}; a &= true || false; a &= 1234; cout << boolalpha << a << endl; bool b = {true}; b &= true || false; b &= 1234; cout << boolalpha << b << endl; }
Wyjście z Visual C ++ 11.0 i g ++ 4.7.1:
Przyczyną różnicy w wynikach jest to, że poziom bitów
&=
nie zapewnia konwersji nabool
argument po prawej stronie.Które z tych wyników chcesz wykorzystać
&=
?Jeśli to pierwsze,
true
lepiej zdefiniuj operator (np. Jak wyżej) lub nazwaną funkcję, albo użyj jawnej konwersji wyrażenia po prawej stronie, albo napisz aktualizację w całości.źródło
bitand
,bitor
,xor
icompl
. Myślę, że dlatego użyłem określenia „czytelny”. Oczywiście czytelność np.compl 42
Jest subiektywna. ;-)bool
” jest tylko konsekwencją konwencji, a nie gwarancją, którą język C ++ faktycznie daje ty.+
gwarantuje konwersję swoich argumentów naunsigned
, ale to nie zapobiegaunsigned int n=-1; n+=3.14;
faktycznemu użyciu operacji dodawaniadouble
(czy jest tofloat
?). (Porównaj swój&=
przykład.)W przeciwieństwie do odpowiedzi Patricka, C ++ nie ma
^^
operatora do wykonywania wyłącznego zwarcia lub. Jeśli pomyślisz o tym przez chwilę, posiadanie^^
operatora i tak nie miałoby sensu: w przypadku wyłącznego lub wynik zawsze zależy od obu operandów. Jednak ostrzeżenie Patricka dotyczącebool
typów innych niż „boolowskie” sprawdza się równie dobrze w porównaniu1 & 2
z1 && 2
. Jednym z klasycznych przykładów jestGetMessage()
funkcja systemu Windows , która zwraca stan trójstanowyBOOL
: niezerowy0
, lub-1
.Używanie
&
zamiast&&
i|
zamiast||
nie jest rzadką literówką, więc jeśli robisz to celowo, zasługuje na komentarz wyjaśniający dlaczego.źródło
^^
Operator będzie nadal użyteczny niezależnie od rozpatrzenia zwarcia. To by a) oceniało operandy w kontekście boolowskim ib) gwarantowało zwrócenie 1 lub 0.inline bool XOR(bool a,bool b) { return a!=b; }
i uzyskać to, czego chcesz, z wyjątkiem tego, że jest to funkcja, a nie operator (wrostek). Możesz też bezpośrednio użyć!=
lub przeciążać innego operatora o tym znaczeniu, ale wtedy oczywiście musisz bardzo uważać, aby przypadkowo nie użyć niezamierzonego przeciążenia o tej samej nazwie. I nawiasem mówiąc||
i&&
wróćtrue
lubfalse
nie1
lub0
.!
,||
i&&
ocenić swoje argumenty w kontekście logicznym jest tylko dlatego, że te podmioty są rzadko przeciążony przyjąć inne rodzaje; o ile wiem, język dopuszcza takie przeciążenia, a zatem nie gwarantuje oceny argumentów w kontekście boolowskim.Patrick zrobił słuszne uwagi i nie zamierzam ich powtarzać. Mogę jednak zasugerować ograniczenie instrukcji `` if '' do czytelnego angielskiego wszędzie tam, gdzie to możliwe, używając dobrze nazwanych zmiennych boolowskich.Na przykład, i jest to użycie operatorów logicznych, ale możesz równie dobrze użyć bitowych i odpowiednio nazwać boole:
bool onlyAIsTrue = (a && !b); // you could use bitwise XOR here bool onlyBIsTrue = (b && !a); // and not need this second line if (onlyAIsTrue || onlyBIsTrue) { .. stuff .. }
Możesz pomyśleć, że użycie wartości logicznej wydaje się niepotrzebne, ale pomaga w dwóch głównych rzeczach:
EDYCJA: Nie powiedziałeś wyraźnie, że chciałeś warunków warunkowych dla stwierdzeń „jeśli” (chociaż wydaje się to najbardziej prawdopodobne), takie było moje założenie. Ale moja sugestia dotycząca pośredniej wartości logicznej jest nadal aktualna.
źródło
Korzystanie z operacji bitowych dla bool pomaga zaoszczędzić niepotrzebną logikę przewidywania rozgałęzień przez procesor, wynikającą z instrukcji „cmp” wprowadzonej przez operacje logiczne.
Zastąpienie operacji logicznych operacjami bitowymi (gdzie wszystkie operandy są typu bool) generuje bardziej wydajny kod oferujący ten sam wynik. W idealnym przypadku wydajność powinna przeważać nad wszystkimi korzyściami zwarciowymi, które można wykorzystać przy zamawianiu przy użyciu operacji logicznych.
Może to spowodować, że kod będzie nieco nieczytelny, aczkolwiek programista powinien skomentować go, podając powody, dla których tak się stało.
źródło
IIRC, wiele kompilatorów C ++ będzie ostrzegać podczas próby rzutowania wyniku operacji bitowej jako bool. Musiałbyś użyć rzutowania typu, aby kompilator był zadowolony.
Użycie operacji bitowej w wyrażeniu if mogłoby służyć tej samej krytyce, chociaż być może nie przez kompilator. Każda wartość niezerowa jest uznawana za prawdę, więc coś w rodzaju „if (7 i 3)” będzie prawdziwe. Takie zachowanie może być akceptowalne w Perlu, ale C / C ++ to bardzo wyraźne języki. Myślę, że brwi Spocka to należyta staranność. :) Dodałbym „== 0” lub „! = 0”, aby było doskonale jasne, jaki był Twój cel.
Ale w każdym razie brzmi to jak osobiste preferencje. Przepuściłbym kod za pomocą linta lub podobnego narzędzia i sprawdziłbym, czy również uważa, że to nierozsądna strategia. Osobiście brzmi to jak błąd w kodowaniu.
źródło