Obecnie uczę się C ++ z książką C ++ Primer, a jedno z ćwiczeń w książce to:
Wyjaśnij, co robi to wyrażenie:
someValue ? ++x, ++y : --x, --y
Co wiemy? Wiemy, że operator trójskładnikowy ma wyższy priorytet niż operator przecinka. W przypadku operatorów binarnych było to dość łatwe do zrozumienia, ale z operatorem trójskładnikowym trochę się zmagam. W przypadku operatorów binarnych „mający wyższy priorytet” oznacza, że możemy używać nawiasów wokół wyrażenia o wyższym priorytecie i nie zmieni to wykonania.
Dla operatora trójskładnikowego zrobiłbym:
(someValue ? ++x, ++y : --x, --y)
w efekcie uzyskując ten sam kod, który nie pomaga mi w zrozumieniu, w jaki sposób kompilator pogrupuje kod.
Jednak z testów za pomocą kompilatora C ++ wiem, że wyrażenie kompiluje się i nie wiem, co :
operator mógłby sam oznaczać. Tak więc kompilator wydaje się poprawnie interpretować operator trójskładnikowy.
Następnie wykonałem program na dwa sposoby:
#include <iostream>
int main()
{
bool someValue = true;
int x = 10, y = 10;
someValue ? ++x, ++y : --x, --y;
std::cout << x << " " << y << std::endl;
return 0;
}
Prowadzi do:
11 10
Z drugiej strony z someValue = false
nim drukuje:
9 9
Dlaczego kompilator C ++ miałby generować kod, który dla prawdziwej gałęzi operatora trójskładnikowego tylko zwiększa x
, podczas gdy dla fałszywej gałęzi trójskładnikowej zmniejsza zarówno x
i y
?
Poszedłem nawet do umieszczenia nawiasów wokół prawdziwej gałęzi w następujący sposób:
someValue ? (++x, ++y) : --x, --y;
ale nadal skutkuje 11 10
.
źródło
?
jest operatorem warunkowym . Termin operator trójskładnikowy oznacza po prostu operator z trzema argumentami. Operator warunkowy jest jednym z przykładów operatorów trójskładnikowych, ale język mógłby (teoretycznie) mieć wiele operatorów trójskładnikowych.Odpowiedzi:
Jak powiedział @Rakete w swojej doskonałej odpowiedzi, jest to trudne. Chciałbym do tego trochę dodać.
Operator trójskładnikowy musi mieć postać:
Mamy więc następujące mapowania:
someValue
: logiczne lub wyrażenie++x, ++y
: wyrażenie--x, --y
czy tylko--x
?W rzeczywistości dzieje się tak tylko
--x
dlatego, że wyrażenie przypisania nie może być analizowane jako dwa wyrażenia oddzielone przecinkiem (zgodnie z regułami gramatycznymi C ++), więc--x, --y
nie może być traktowane jako wyrażenie przypisania .Co powoduje, że część wyrażenia trójskładnikowego (warunkowego) wygląda następująco:
Ze względu na czytelność pomocne może
++x,++y
być rozważenie obliczenia w taki sposób, jakby był w nawiasach(++x,++y)
; wszystko, co znajduje się pomiędzy?
i:
będzie sekwencjonowane po warunku. (Umieszczę je w nawiasach do końca postu).i oceniane w tej kolejności:
someValue?
(++x,++y)
lub--x
(w zależności odbool
wyniku 1.)To wyrażenie jest następnie traktowane jako lewe wyrażenie podrzędne do operatora przecinka, przy czym prawe wyrażenie podrzędne jest następujące
--y
:Co oznacza, że lewa strona jest wyrażeniem wartości odrzuconej , co oznacza, że jest zdecydowanie oceniana, ale potem oceniamy prawą stronę i zwracamy ją.
Więc co się dzieje, kiedy
someValue
jesttrue
?(someValue?(++x,++y):--x)
wykonuje i zwiększax
orazy
być11
i11
--y
który następnie zmniejszay
się z powrotem do10
Aby „naprawić” to zachowanie, możesz pogrupować
--x, --y
je w nawiasy, aby przekształcić je w wyrażenie podstawowe, które jest prawidłowym wpisem dla wyrażenia przypisania *:* To dość zabawny długi łańcuch, który łączy wyrażenie przypisania z powrotem z wyrażeniem podstawowym:
wyrażenie-przypisania --- (może składać się z) -> wyrażenie-warunkowe -> wyrażenie - logiczne -> wyrażenie - logiczne -> wyrażenie - włączające -> wyrażenie - wyłączne - -> wyrażenie-i -> wyrażenie-równości -> wyrażenie-relacyjne -> wyrażenie-przesunięcia -> wyrażenie-addytywne -> wyrażenie-multiplikatywne -> wyrażenie-pm -> wyrażenie-rzutowane -> jednoargumentowe-wyrażenie -> postfiks-wyrażenie -> podstawowe-wyrażenie
źródło
{ ... }
można je traktować jako wyrażenie), mam teraz odpowiedź => chodzi o to, aby uniknąć konieczności wprowadzania operatora przecinka, który zachowuje się w tak podstępny sposób.assignment-expression
łańcuchu?Wow, to trudne.
Kompilator widzi twoje wyrażenie jako:
Operator trójskładnikowy potrzebuje a
:
, nie może stać sam w tym kontekście, ale po nim nie ma powodu, dla którego przecinek miałby należeć do fałszywego przypadku.Teraz może mieć większy sens, dlaczego otrzymujesz takie wyjście. Jeśli
someValue
to prawda, to++x
,++y
i--y
zostanie wykonany, które nie skutecznie zmienićy
, ale dodaje jeden dox
.Jeśli
someValue
jest fałszywe, to--x
i--y
są wykonywane, zmniejszając je o jeden.źródło
Źle zinterpretowałeś, co się stało. Prawdziwa gałąź zwiększa zarówno
x
iy
. Jednak,y
jest dekrementowany natychmiast po tym, bezwarunkowo.Oto jak to się dzieje: ponieważ operator warunkowy ma wyższy priorytet niż operator przecinka w C ++ , kompilator analizuje wyrażenie w następujący sposób:
Zwróć uwagę na słowo „osierocony”
--y
po przecinku. To właśnie prowadzi do ubytkuy
, który został początkowo zwiększony.Byłeś na właściwej ścieżce, ale umieściłeś w nawiasach niewłaściwą gałąź: możesz to naprawić, umieszczając w nawiasach gałąź else, na przykład:
Demo (wydruki 11 11)
źródło
Twoim problemem jest to, że trójskładnikowe wyrażenie nie ma tak naprawdę wyższego priorytetu niż przecinek. W rzeczywistości C ++ nie może być dokładnie opisany po prostu przez pierwszeństwo - i to właśnie interakcja między operatorem trójskładnikowym a przecinkiem jest przyczyną podziału.
jest traktowany jako:
(przecinek zachowuje się tak, jakby miał wyższy priorytet). Z drugiej strony,
jest traktowany jako:
a operator trójskładnikowy ma wyższy priorytet.
źródło
Punkt, który został przeoczony w odpowiedziach (choć został poruszony w komentarzach), to fakt, że operator warunkowy jest niezmiennie używany (zamierzony przez projekt?) W rzeczywistym kodzie jako skrót do przypisywania jednej z dwóch wartości do zmiennej.
Zatem szerszy kontekst wyglądałby tak:
Co jest absurdalne, więc zbrodnie są wielorakie:
źródło
if
(na przykład wyrażenie inkrementacji w pętli for). Większy kontekst może byćfor (x = 0, y=0; x+y < 100; someValue?(++x, ++y) :( --x, --y))
z pętlą, którą można modyfikowaćx
iy
niezależnie.