Ich lerne gerade C ++ mit dem Buch C ++ Primer und eine der Übungen im Buch ist:
Erklären Sie, was der folgende Ausdruck bewirkt:
someValue ? ++x, ++y : --x, --y
Was wissen wir? Wir wissen, dass der ternäre Operator eine höhere Priorität hat als der Kommaoperator. Mit binären Operatoren war das ziemlich leicht zu verstehen, aber mit dem ternären Operator habe ich ein bisschen Probleme. Bei binären Operatoren bedeutet "höhere Priorität", dass wir Klammern um den Ausdruck mit höherer Priorität verwenden können und die Ausführung nicht geändert wird.
Für den ternären Operator würde ich tun:
(someValue ? ++x, ++y : --x, --y)
Dies führt effektiv zu demselben Code, der mir nicht hilft zu verstehen, wie der Compiler den Code gruppiert.
Durch das Testen mit einem C ++ - Compiler weiß ich jedoch, dass der Ausdruck kompiliert wird und ich weiß nicht, wofür ein :
Operator für sich stehen könnte. Der Compiler scheint den ternären Operator also richtig zu interpretieren.
Dann habe ich das Programm auf zwei Arten ausgeführt:
#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;
}
Ergebnisse in:
11 10
Während auf der anderen Seite someValue = false
damit druckt:
9 9
Warum sollte der C ++ - Compiler Code generieren, der für den wahren Zweig des ternären Operators nur inkrementiert x
, während er für den falschen Zweig des ternären Operators sowohl x
als auch dekrementiert y
?
Ich ging sogar so weit, den wahren Zweig wie folgt in Klammern zu setzen:
someValue ? (++x, ++y) : --x, --y;
aber es ergibt sich immer noch 11 10
.
quelle
?
ist der bedingte Operator . Der Begriff ternärer Operator bedeutet einfach einen Operator mit drei Operanden. Der bedingte Operator ist ein Beispiel für einen ternären Operator, aber eine Sprache könnte (theoretisch) mehrere ternäre Operatoren haben.Antworten:
Wie @Rakete in ihrer ausgezeichneten Antwort sagte, ist dies schwierig. Ich möchte das noch etwas ergänzen.
Der ternäre Operator muss das folgende Formular haben:
Wir haben also folgende Zuordnungen:
someValue
: logisch-oder-Ausdruck++x, ++y
: Ausdruck--x, --y
oder nur--x
?In der Tat ist es nur
--x
daran, dass ein Zuweisungsausdruck nicht als zwei durch Komma getrennte Ausdrücke (gemäß den Grammatikregeln von C ++) analysiert werden--x, --y
kann und daher nicht als Zuweisungsausdruck behandelt werden kann .Was dazu führt, dass der ternäre (bedingte) Ausdrucksteil so aussieht:
Aus Gründen der Lesbarkeit kann es hilfreich
++x,++y
sein, die Berechnung in Klammern zu betrachten(++x,++y)
. Alles, was zwischen?
und enthalten ist,:
wird nach der Bedingung sequenziert . (Ich werde sie für den Rest des Beitrags in Klammern setzen).und in dieser Reihenfolge ausgewertet:
someValue?
(++x,++y)
oder--x
(abhängig vombool
Ergebnis von 1.)Dieser Ausdruck wird dann als linker Unterausdruck für einen Kommaoperator behandelt, wobei der rechte Unterausdruck lautet
--y
wie :Was bedeutet, dass die linke Seite ein Ausdruck mit verworfenem Wert ist , was bedeutet, dass er definitiv ausgewertet wird, aber dann bewerten wir die rechte Seite und geben diesen zurück.
Also, was passiert wann
someValue
isttrue
?(someValue?(++x,++y):--x)
führt aus und erhöhtx
undy
zu sein11
und11
--y
Kommaoperatorsy
aus :, der dann wieder auf dekrementiert10
Zu „reparieren“ das Verhalten, können Sie Gruppe
--x, --y
mit Klammern in einen verwandeln primären Ausdruck , die ist ein gültiger Eintrag für eine Zuordnung Ausdruck *:* Es ist eine ziemlich lustige lange Kette, die einen Zuweisungsausdruck wieder mit einem primären Ausdruck verbindet:
Zuweisungsausdruck --- (kann bestehen aus) -> bedingter Ausdruck -> logisch-oder-Ausdruck -> logisch-und-Ausdruck -> inklusive-oder-Ausdruck -> exklusiv-oder-Ausdruck - -> und-Ausdruck -> Gleichheitsausdruck -> relationaler Ausdruck -> Verschiebungsausdruck -> additiver Ausdruck -> multiplikativer Ausdruck -> pm-Ausdruck -> Besetzungsausdruck -> unärer Ausdruck -> Postfix-Ausdruck -> primärer Ausdruck
quelle
{ ... }
sie als Ausdruck behandelt werden können), habe ich jetzt eine Antwort => Es soll vermieden werden, einen Kommaoperator einzuführen, der sich so knifflig verhält.assignment-expression
Kette zu lesen ?Wow, das ist schwierig.
Der Compiler sieht Ihren Ausdruck wie folgt:
Der ternäre Operator benötigt a
:
, er kann in diesem Zusammenhang nicht für sich allein stehen, aber danach gibt es keinen Grund, warum das Komma zum falschen Fall gehören sollte.Jetzt ist es vielleicht sinnvoller, warum Sie diese Ausgabe erhalten. Wenn
someValue
wahr ist, dann++x
,++y
und--y
ausgeführt werden sollen , die nicht effektiv ändern tut ,y
sondern fügt manx
.Wenn
someValue
falsch ist, werden--x
und--y
ausgeführt, wobei beide um eins dekrementiert werden.quelle
Sie haben falsch interpretiert, was passiert ist. Der True-Branch erhöht sowohl
x
als auchy
. Jedoch,y
wird erniedrigt unmittelbar danach, bedingungslos.Dies geschieht folgendermaßen: Da der bedingte Operator in C ++ eine höhere Priorität als der Kommaoperator hat, analysiert der Compiler den Ausdruck wie folgt:
Beachten Sie das "verwaiste"
--y
nach dem Komma. Dies führt zu einer Dekrementierungy
, die ursprünglich erhöht wurde.Sie waren auf dem richtigen Weg, haben aber einen falschen Zweig in Klammern gesetzt: Sie können dies beheben, indem Sie den else-Zweig wie folgt in Klammern setzen:
Demo (druckt 11 11)
quelle
Ihr Problem ist, dass der ternäre Ausdruck nicht wirklich eine höhere Priorität als Komma hat. Tatsächlich kann C ++ nicht einfach durch Vorrang genau beschrieben werden - und es ist genau die Interaktion zwischen dem ternären Operator und dem Komma, in der es zusammenbricht.
wird behandelt als:
(Komma verhält sich so, als hätte es eine höhere Priorität). Andererseits,
wird behandelt als:
und der ternäre Operator hat eine höhere Priorität.
quelle
Ein Punkt, der in den Antworten übersehen wurde (obwohl er in Kommentaren angesprochen wurde), ist, dass der bedingte Operator im realen Code immer als Verknüpfung zum Zuweisen eines von zwei Werten zu einer Variablen verwendet wird (beabsichtigt durch Design?).
Der größere Kontext wäre also:
Was auf den ersten Blick absurd ist, so dass die Verbrechen vielfältig sind:
quelle
if
(z. B. den Inkrementausdruck in einer for-Schleife). Der größere Kontext könnte durchausfor (x = 0, y=0; x+y < 100; someValue?(++x, ++y) :( --x, --y))
eine Schleife sein, die modifiziertx
undy
unabhängig voneinander sein kann.