Wie funktioniert der Kommaoperator in C ++?
Zum Beispiel, wenn ich das tue:
a = b, c;
Entspricht a b oder c?
(Ja, ich weiß, dass dies einfach zu testen ist. Dokumentieren Sie hier einfach, damit jemand die Antwort schnell findet.)
Update: Diese Frage hat bei Verwendung des Komma-Operators eine Nuance aufgedeckt. Nur um dies zu dokumentieren:
a = b, c; // a is set to the value of b!
a = (b, c); // a is set to the value of c!
Diese Frage wurde tatsächlich von einem Tippfehler im Code inspiriert. Was sein sollte
a = b;
c = d;
Wurde zu
a = b, // <- Note comma typo!
c = d;
c++
comma-operator
Joe Schneider
quelle
quelle
a = (b, c);
.a = b, c = d;
tatsächlich die gleiche Leistung wie beabsichtigta = b; c = d;
?b
undd
Funktionsbewertungen sind, die einen gemeinsamen Zustand verwenden (und ändern), wird die Ausführungsreihenfolge erst definiertC++17
.Antworten:
Es wäre gleich
b
.Der Kommaoperator hat eine niedrigere Priorität als die Zuweisung.
quelle
Beachten Sie, dass der Komma-Operator in C ++ möglicherweise überladen ist. Das tatsächliche Verhalten kann daher sehr unterschiedlich zu dem erwarteten sein.
Beispielsweise verwendet Boost.Spirit den Komma-Operator sehr geschickt, um Listeninitialisierer für Symboltabellen zu implementieren. Somit ist die folgende Syntax möglich und sinnvoll:
Beachten Sie, dass der Code aufgrund der Priorität des Operators (absichtlich!) Identisch mit ist
Das heißt, der erste aufgerufene Operator
keywords.operator =("and")
gibt ein Proxy-Objekt zurück, für das die verbleibendenoperator,
s aufgerufen werden:quelle
char[]
, der nicht überladen werden kann. Der Code ruft absichtlich zuerst dasoperator=
und anschließendoperator,
für jedes verbleibende Element auf.Der Kommaoperator hat die niedrigste Priorität aller C / C ++ - Operatoren. Daher ist es immer der letzte, der an einen Ausdruck bindet, was Folgendes bedeutet:
ist äquivalent zu:
Eine weitere interessante Tatsache ist, dass der Kommaoperator einen Sequenzpunkt einführt . Dies bedeutet, dass der Ausdruck:
Es wird garantiert, dass die drei Unterausdrücke ( a + b , c () und d ) der Reihe nach ausgewertet werden. Dies ist wichtig, wenn sie Nebenwirkungen haben. Normalerweise dürfen Compiler Unterausdrücke in beliebiger Reihenfolge auswerten. Zum Beispiel in einem Funktionsaufruf:
Argumente können in beliebiger Reihenfolge ausgewertet werden. Beachten Sie, dass die Kommas im Funktionsaufruf keine Operatoren sind. Sie sind Trennzeichen.
quelle
,
eine so niedrige Priorität hat, dass es sogar hinter sich selbst zurückbleibt ;) ... Das heißt: Komma als Operator hat eine niedrigere Priorität als Komma als Trennzeichen . Wenn Sie also den Komma als Operator in einem einzelnen Funktionsargument, einer Variablenzuweisung oder einer anderen durch Kommas getrennten Liste verwenden möchten, müssen Sie Klammern verwenden, z. B.:int a = 1, b = 2, weirdVariable = (++a, b), d = 4;
Der Kommaoperator:
Eine Standardversion des Kommaoperators ist für alle Typen (integriert und benutzerdefiniert) definiert und funktioniert wie folgt
exprA , exprB
:exprA
wird ausgewertetexprA
wird ignoriertexprB
wird ausgewertetexprB
wird als Ergebnis des gesamten Ausdrucks zurückgegebenBei den meisten Operatoren kann der Compiler die Ausführungsreihenfolge auswählen, und es ist sogar erforderlich, die Ausführung zu überspringen, wenn dies das Endergebnis nicht beeinflusst (z. B.
false && foo()
wird der Aufruf von übersprungenfoo
). Dies ist jedoch beim Komma-Operator nicht der Fall und die obigen Schritte werden immer ausgeführt * .In der Praxis funktioniert der Standard-Kommaoperator fast genauso wie ein Semikolon. Der Unterschied besteht darin, dass zwei durch ein Semikolon getrennte Ausdrücke zwei separate Anweisungen bilden, während die Kommatrennung alle als einen einzigen Ausdruck enthält. Aus diesem Grund wird in den folgenden Szenarien manchmal ein Kommaoperator verwendet:
if( HERE )
for
Schleifefor ( HERE ; ; )
if (foo) HERE ;
(Bitte tun Sie das nicht, es ist wirklich hässlich!)Wenn eine Anweisung kein Ausdruck ist, kann das Semikolon nicht durch ein Komma ersetzt werden. Zum Beispiel sind diese nicht erlaubt:
(foo, if (foo) bar)
(if
ist kein Ausdruck)In Ihrem Fall haben wir:
a=b, c;
Dies entspricht dera=b; c;
Annahme, dassa
es sich um einen Typ handelt, der den Kommaoperator nicht überlastet.a = b, c = d;
Dies entspricht dera=b; c=d;
Annahme, dassa
es sich um einen Typ handelt, der den Kommaoperator nicht überlastet.Beachten Sie, dass nicht jedes Komma ein Kommaoperator ist. Einige Kommas, die eine ganz andere Bedeutung haben:
int a, b;
--- Die Liste der Variablendeklarationen ist durch Kommas getrennt, dies sind jedoch keine Kommaoperatorenint a=5, b=3;
--- Dies ist auch eine durch Kommas getrennte Variablendeklarationslistefoo(x,y)
--- durch Kommas getrennte Argumentliste. In der Tatx
undy
kann in beliebiger Reihenfolge ausgewertet werden!FOO(x,y)
--- durch Kommas getrennte Makroargumentlistefoo<a,b>
--- durch Kommas getrennte Vorlagenargumentlisteint foo(int a, int b)
--- durch Kommas getrennte ParameterlisteFoo::Foo() : a(5), b(3) {}
--- durch Kommas getrennte Initialisiererliste in einem Klassenkonstruktor* Dies gilt nicht ganz, wenn Sie Optimierungen anwenden. Wenn der Compiler erkennt, dass ein bestimmter Code absolut keine Auswirkungen auf den Rest hat, werden die unnötigen Anweisungen entfernt.
Weiterführende Literatur: http://en.wikipedia.org/wiki/Comma_operator
quelle
operator ,
Überlastung die Assoziativitätsgarantien verlieren (genau wie Sie die Kurzschlusseigenschaften deroperator&&
undoperator||
wenn sie überlastet sind) verlieren ?a, b, c
bedeutet immer(a, b), c
und niea, (b, c)
. Die letztere Interpretation könnte sogar zu Kompilierungsfehlern führen, wenn Elemente unterschiedlichen Typs sind. Nach welcher Reihenfolge können Sie die Argumente bewerten? Da bin ich mir nicht sicher, aber vielleicht haben Sie Recht: Es kann vorkommen, dassc
das vorher ausgewertet wird,(a, b)
auch wenn das Komma linksassoziativ ist.struct Foo { Foo() : a(5), b(3) {} int b; int a; }
evauliertb(3)
vorhera(5)
. Dies ist wichtig, wenn Ihre Liste so ist :Foo() : a(5), b(a) {}
. b wird nicht auf 5 gesetzt, sondern auf den nicht initialisierten Wert von a, vor dem Ihr Compiler möglicherweise warnt oder nicht.Der Wert von
a
wird seinb
, aber der Wert des Ausdrucks wird seinc
. Das ist ina wäre gleich
b
undd
wäre gleichc
.quelle
a = b; d = c;
?Der Wert von b wird a zugewiesen. Nichts wird passieren c
quelle
Der Wert von a ist gleich b, da der Kommaoperator eine niedrigere Priorität hat als der Zuweisungsoperator.
quelle
Ja Der Kommaoperator hat eine niedrige Priorität als der Zuweisungsoperator
Ausgabe: i = 3
Da der Kommaoperator immer den Wert ganz rechts zurückgibt.
Im Falle eines Kommaoperators mit Zuweisungsoperator:
Ausgabe: i = 1
Wie wir wissen, hat der Kommaoperator eine niedrigere Priorität als die Zuweisung .....
quelle
i = 1;
in dieser Zeile zu stehen?Das Wichtigste zuerst: Komma ist eigentlich kein Operator, für den Compiler ist es nur ein Token, das im Kontext mit anderen Token eine Bedeutung erhält .
Was bedeutet das und warum sich die Mühe machen?
Beispiel 1:
Um den Unterschied zwischen der Bedeutung desselben Tokens in einem anderen Kontext zu verstehen, sehen wir uns dieses Beispiel an:
Normalerweise wird ein C ++ Anfänger würde denken , dass dieser Ausdruck könnte / würde die Dinge vergleichen , aber es ist absolut falsch, die Bedeutung der
<
,>
und,
Token depent auf dem Nutzungskontext.Die korrekte Interpretation des obigen Beispiels ist natürlich, dass es sich um eine Instanziierung einer Vorlage handelt.
Beispiel 2:
Wenn wir eine typische for-Schleife mit mehr als einer Initialisierungsvariablen und / oder mehr als einem Ausdruck schreiben, die nach jeder Iteration der Schleife ausgeführt werden soll, verwenden wir auch Komma:
Die Bedeutung des Kommas hängt vom Verwendungskontext ab, hier ist es der Kontext der
for
Konstruktion.Was bedeutet eigentlich ein Komma im Kontext?
Um es noch komplizierter zu machen (wie immer in C ++), kann der Kommaoperator selbst überladen werden (dank Konrad Rudolph für den Hinweis).
Um auf die Frage zurückzukommen, den Kodex
bedeutet für den Compiler so etwas wie
weil die Priorität des
=
Tokens / Operators höher ist als die Priorität des,
Tokens.und dies wird im Kontext wie interpretiert
(Beachten Sie, dass die Interpretation vom Kontext abhängt, hier weder ein Funktions- / Methodenaufruf noch eine Template-Instanziierung.)
quelle