Czy norma ANSI nakazuje zwarcie operatorów logicznych w języku C lub C ++?
Jestem zdezorientowany, ponieważ przypominam sobie książkę K&R, w której napisano, że twój kod nie powinien polegać na zwarciu tych operacji, ponieważ mogą nie. Czy ktoś mógłby wskazać, gdzie w standardzie mówi się, że operacje logiczne są zawsze zwarte? Najbardziej interesuje mnie C ++, odpowiedź również na C byłaby świetna.
Pamiętam również, że przeczytałem (nie pamiętam gdzie), że kolejność oceny nie jest ściśle zdefiniowana, więc Twój kod nie powinien zależeć ani zakładać, że funkcje w wyrażeniu byłyby wykonywane w określonej kolejności: na końcu instrukcji wszystkie funkcje, do których się odwołujesz zostanie wywołana, ale kompilator ma swobodę wyboru najbardziej wydajnej kolejności.
Czy norma wskazuje kolejność oceny tego wyrażenia?
if( functionA() && functionB() && functionC() ) cout<<"Hello world";
źródło
Odpowiedzi:
Tak, kolejność zwarć i oceny jest wymagana dla operatorów
||
i&&
zarówno w standardach C, jak i C ++.Standard C ++ mówi (w standardzie C powinna być klauzula równoważna):
W C ++ istnieje dodatkowa pułapka: zwarcie NIE ma zastosowania do typów, które przeciążają operatory
||
i&&
.Zwykle nie zaleca się przeciążania tych operatorów w C ++, chyba że masz bardzo szczegółowe wymagania. Możesz to zrobić, ale może to zepsuć oczekiwane zachowanie w kodzie innych osób, zwłaszcza jeśli te operatory są używane pośrednio przez tworzenie wystąpień szablonów z typem przeciążającym te operatory.
źródło
Ocena zwarcia i kolejność oceny to obowiązkowy standard semantyczny zarówno w C, jak i C ++.
Gdyby tak nie było, taki kod nie byłby powszechnym idiomem
Sekcja 6.5.13 Operator logiczny AND specyfikacji C99 (łącze PDF) mówi
Podobnie mówi sekcja 6.5.14 Operator logiczny OR
Podobne sformułowania można znaleźć w standardach C ++, patrz sekcja 5.14 w tej wersji roboczej . Jak zaznacza warcaby w innej odpowiedzi, jeśli przesłonisz && lub ||, to oba operandy muszą zostać ocenione, ponieważ stanie się zwykłym wywołaniem funkcji.
źródło
Tak, nakazuje to (zarówno kolejność oceny, jak i zwarcie). W naszym przykładzie, jeśli wszystkie funkcje zwracają wartość true, kolejność wywołań pochodzi ściśle od functionA, następnie functionB, a następnie functionC. Używany do tego jak
To samo dotyczy operatora przecinka:
Mówi się od lewego i prawego argumentu instrukcji
&&
,||
,,
a pomiędzy pierwszą i drugą / trzecią operandu?:
(operator warunkowych) stanowi „punkt sekwencji”. Wszelkie skutki uboczne są oceniane całkowicie przed tym punktem. Więc to jest bezpieczne:Zauważ, że operatora przecinka nie należy mylić z przecinkiem składniowym używanym do oddzielania rzeczy:
Standard C ++ mówi w
5.14/1
:A w
5.15/1
:Mówi o obu obok tych:
Oprócz tego,
1.9/18
mówiźródło
Prosto ze starego dobrego K&R:
źródło
Bądź bardzo ostrożny.
W przypadku typów podstawowych są to operatory skrótów.
Ale jeśli zdefiniujesz te operatory dla własnej klasy lub typów wyliczeń, nie będą one skrótami. Z powodu tej semantycznej różnicy w ich użyciu w tych różnych okolicznościach nie zaleca się definiowania tych operatorów.
Dla typów podstawowych
operator &&
ioperator ||
dla typów podstawowych kolejność oceny jest od lewej do prawej (w przeciwnym razie krótkie cięcie byłoby trudne :-). Ale w przypadku przeciążonych operatorów, które definiujesz, są to w zasadzie cukier syntaktyczny do zdefiniowania metody, a zatem kolejność oceny parametrów jest nieokreślony.źródło
Twoje pytanie sprowadza się do pierwszeństwa i skojarzenia operatorów C ++ . Zasadniczo w wyrażeniach z wieloma operatorami i bez nawiasów kompilator konstruuje drzewo wyrażeń, postępując zgodnie z tymi regułami.
Aby uzyskać pierwszeństwo, jeśli masz coś takiego
A op1 B op2 C
, możesz pogrupować rzeczy jako albo(A op1 B) op2 C
alboA op1 (B op2 C)
. Jeśliop1
ma wyższy priorytet niżop2
, otrzymasz pierwsze wyrażenie. W przeciwnym razie zdobędziesz drugi.W przypadku asocjatywności, gdy masz coś podobnego
A op B op C
, możesz ponownie zgrupować cienkie litery jako(A op B) op C
lubA op (B op C)
. Jeśliop
opuścił łączność, otrzymujemy pierwsze wyrażenie. Jeśli ma właściwą łączność, kończymy z drugim. Działa to również w przypadku operatorów na tym samym poziomie pierwszeństwa.W tym konkretnym przypadku
&&
ma wyższy priorytet niż||
, więc wyrażenie zostanie ocenione jako(a != "" && it == seqMap.end()) || isEven
.Sama kolejność jest „od lewej do prawej” w postaci drzewa wyrażeń. Więc najpierw dokonamy oceny
a != "" && it == seqMap.end()
. Jeśli to prawda, całe wyrażenie jest prawdziwe, w przeciwnym razie przejdziemy doisEven
. Procedura ta oczywiście powtarza się rekurencyjnie wewnątrz lewego podwyrażenia.Ciekawostki, ale pojęcie pierwszeństwa ma swoje korzenie w notacji matematycznej. To samo dzieje się w
a*b + c
przypadku, gdy*
ma wyższy priorytet niż+
.Jeszcze bardziej interesujące / niejasne, dla nieprecyzyjnego wyrażenia
A1 op1 A2 op2 ... opn-1 An
, w którym wszystkie operatory mają ten sam priorytet, liczba drzew wyrażeń binarnych, które moglibyśmy utworzyć, jest określona przez tak zwane liczby katalońskie . W przypadku dużychn
rosną one niezwykle szybko. reźródło
Jeśli ufasz Wikipedii:
C (język programowania)
źródło