operator bool ++ i -

104

Dzisiaj podczas pisania kodu Visual C ++ natknąłem się na coś, co mnie zaskoczyło. Wygląda na to, że C ++ obsługuje ++ (inkrementacja) dla bool, ale nie - (dekrementacja). Czy to tylko przypadkowa decyzja, czy jest jakiś powód?

To kompiluje:

static HMODULE hMod = NULL;
static bool once = false;
if (!once++)
    hMod = LoadLibrary("xxx");

To nie:

static HMODULE hMod = NULL;
static bool once = true;
if (once--)
    hMod = LoadLibrary("xxx");
Suma
źródło
2
hm, to samo dla kompilatora xcode i gcc
Vladimir
Tak, ++oncei once++pracuj z gcc, ale nie z dekrementami.
Justin Ardini
Może przeformatuj „historię” zamiast „operatora-słowa kluczowego”, więc to jest zgrupowane ze wszystkimi innymi zabawnymi wyjaśnieniami, dlaczego różne szalone rzeczy są rozsądne, jeśli weźmiesz pod uwagę historię? :)
Jon Hanna
Zauważ, że od C ++ 17 operator preinkrementacji dla booljest przestarzały, souce .
cogle
można to zastąpić std::exchange(once,false)(uwaga: nie atomic), jeśli chcesz czegoś, co nie jest przestarzałe.
golvok

Odpowiedzi:

90

Pochodzi z historii używania wartości całkowitych jako wartości logicznych.

Jeśli xjest int, ale używam go jako wartości logicznej, zgodnie z if(x)...tym zwiększanie będzie oznaczało, że niezależnie od jego prawdziwej wartości przed operacją, będzie miała wartość prawdziwości truepo niej (z wyjątkiem przepełnienia).

Jednak niemożliwe jest przewidzenie wyniku --danej wiedzy tylko o wartości prawdziwości x, ponieważ może to skutkować false(jeśli wartość całkowa wynosi 1) lub true(jeśli wartość całkowa jest czymś innym - w szczególności obejmuje to 0 [ false] i 2 lub więcej [ true]).

Więc jak krótka ręka ++zadziałała i --nie.

++ jest dozwolone na bools dla zgodności z tym, ale jego użycie jest przestarzałe w standardzie.


Zakłada się, że tylko użyć xjako wartość logiczną, co oznacza, że przelew nie może się zdarzyć, aż zrobiłem ++wystarczająco często, aby spowodować przepełnienie na swój własny. Nawet z char jako używanym typem i CHAR_BITSczymś niskim, jak 5, to 32 razy, zanim to już nie zadziała (to wciąż wystarczający argument, że jest to zła praktyka, nie bronię praktyki, tylko wyjaśniam, dlaczego to działa) w przypadku wersji 32-bitowej intmusielibyśmy oczywiście użyć ++2 ^ 32 razy, zanim pojawi się problem. Z tym, --że spowoduje to tylko falsewtedy, gdy zacznę od wartości 1 for truelub zacznę od 0 i użyłem ++dokładnie raz wcześniej.

Inaczej jest, jeśli zaczniemy z wartością, która znajduje się zaledwie kilka poniżej 0. Istotnie, w takim przypadku może chcemy ++, aby doprowadzić do falsewartości takiej, jak w końcu:

int x = -5;
while(++x)
  doSomething(x);

Jednak ten przykład traktuje xjako intwszędzie oprócz warunku, więc jest równoważny z:

int x = -5;
while(++x != 0)
  doSomething(x);

Co różni się od używania tylko xjako wartości logicznej.

Jon Hanna
źródło
1
Dziękuję Ci. Wspaniale wiedzieć, że nadal mogę udzielać odpowiedzi ludziom takim jak ten, biorąc pod uwagę, ile czasu minęło, odkąd napisałem wiersz w C ++ :)
Jon Hanna
8
Ale gdyby x było -1 (PRAWDA na niektórych platformach, takich jak VB), ++ x byłoby FAŁSZEM.
James Curran
4
@James, w C i C ++ to byłby przypadek, o którym myślałem, kiedy powiedziałem („zakaz przepełnienia”). Właściwie w VB każda wartość niezerowa ma wartość PRAWDA (jak w C), ale ma -1 zamiast 1 w wyniku prawdziwych operacji boolowskich, ponieważ wtedy NIE (PRAWDA) jest FAŁSZ, NIE (FAŁSZ) jest PRAWDA, x LUB TRUE to TRUE, x OR FALSE to x, x AND FALSE to FALSE ix AND TRUE to x itd., Używając tych samych operatorów dla operacji logicznych i bitowych (ponieważ VB zakłada uzupełnienie do dwóch, więc -1 to wszystkie 1 bity). Jednak może to powodować dziwne błędy w VB, jeśli koder nie wychwyci, że 2 (prawda) AND 4 (prawda) daje 0 (fałsz).
Jon Hanna
2
@JonHanna: ANSI C89 był pierwszym standardem C. Komitet ANSI C wymyślił <limits.h>nagłówek i CHAR_BITmakro. Wcześniej, jak przypuszczam, teoretycznie mogły istnieć implementacje, w których charjest węższe niż 8 bitów, ale o ile wiem, nie było żadnych. W szczególności K & R1 (opublikowany w 1978 r.) Wymienia 4 przykładowe implementacje, z których wszystkie mają 8 lub 9 bitów char.
Keith Thompson
1
@JonHanna: Zgodna implementacja języka C musi mieć CHAR_BIT >= 8. Norma nie uwzględnia celów, w przypadku których jest to trudne. (Oczywiście, możesz mieć niezgodną implementację.)
Keith Thompson,
29

ANSI ISO IEC 14882 2003 (c ++ 03):

5.2.6-2

Operand postfiksu - jest dekrementowany analogicznie do operatora postfiksu ++, z wyjątkiem tego, że operand nie powinien być typu bool. [Uwaga: Przy zwiększaniu i zmniejszaniu prefiksu, patrz 5.3.2. ]

I nic dziwnego ...

5.3.2-2

Operand przedrostka - jest modyfikowany przez odjęcie 1. Operand nie może być typu bool. Wymagania dotyczące argumentu przedrostka - i właściwości jego wyniku są poza tym takie same, jak w przypadku przedrostka ++. [Uwaga: Przy zwiększaniu i zmniejszaniu przyrostka, patrz 5.2.6. ]

Również 5.6.2-1 i 5.3.2-1 wspominają, że ++ dla bools powinno być prawdziwe, a załącznik D-1 mówi, że ++ on bools jest przestarzałe.

Nordic Mainframe
źródło
3
@BlueRaja: Zobacz odpowiedź Jona Hanny.
Justin Ardini
9

Ze względów historycznych zostało to poparte. Ale pamiętaj, że ... Użycie operandu typu bool z operatorem ++ jest przestarzałe, patrz sekcja 5.3.2 w standardzie C ++ (n3092)

5.3.2 Przyrost i dekrementacja [wyra.pre.incr]

  • Operand przedrostka ++ jest modyfikowany przez dodanie 1 lub ustawiany na wartość true, jeśli ma wartość bool (to użycie jest przestarzałe). Argument jest modyfikowalną l-wartością. Typ operandu powinien być typem arytmetycznym lub wskaźnikiem do całkowicie zdefiniowanego typu obiektu. Wynikiem jest zaktualizowany operand; jest to lwartość i jest to pole bitowe, jeśli operand jest polem bitowym. Jeśli x nie jest typu bool, wyrażenie ++ x jest równoważne x + = 1 [Uwaga: zobacz omówienie operatorów dodawania (5.7) i przypisania (5.17), aby uzyskać informacje na temat konwersji. —End note]
  • Operand przedrostka - jest modyfikowany przez odjęcie 1. Operand nie może być typu bool. Wymagania dotyczące argumentu przedrostka - i właściwości jego wyniku są poza tym takie same, jak w przypadku przedrostka ++.
Abhay
źródło
3
  • Przy starych standardach (C ++ 98) nie jest to błąd.
  • Wraz z nowymi standardami zwiększanie wartości logicznej jest przestarzałe. (C ++ 11)
  • Możesz użyć inkrementacji na wartości logicznej do C ++ 17.
mustafagonul
źródło