10

The C++11 standard (5,17, expr.ass) besagt, dassDoppelbelegung der gleichen Variablen in einem Ausdruck in C++ 11

In allen Fällen ist die Zuordnung nach dem Wertberechnungs des rechten sequenziert wird und linke Operanden und vor der Wertberechnung von der Zuweisungsausdruck. Im Hinblick auf einen indeterminately sequenzierten Funktionsaufruf, der Betrieb einer Verbindung Zuordnung ist eine einzige Bewertung

Wie ich es verstehe, alle Ausdrücke, die ein Teil der gegebenen Zuordnung sind, werden vor der Zuweisung ausgewertet werden selbst . Diese Regel sollte auch dann funktionieren, wenn ich dieselbe Variable zweimal in derselben Zuweisung ändere, was, wie ich ziemlich sicher bin, vorher undefiniert war.

Wird der angegebene Code:

int a = 0; 
a = (a+=1) = 10; 

if (a == 10) { 
    printf("this is defined"); 
} else { 
    printf("undefined"); 
} 

immer a==10 bewerten?

+0

Randnotiz: Das Überprüfen des Ergebnisses eines möglicherweise nicht definierten Ausdrucks sagt uns nicht, ob es tatsächlich UB ist oder nicht. Es kann UB sein und ein korrektes Ergebnis liefern. – jrok

+3

@jrok das ist ein Beispiel-Code, vielleicht sogar ein SSCCE, wie SO erfordert, dass ich * gültigen Code *. Ich habe es aus Neugier getestet, aber ich merke, dass es nichts beweist; daher habe ich es nicht einmal erwähnt. – Dariusz

+2

@jrok - es kann "korrekte" Ergebnisse erzeugen. Die Zitate sind wichtig. '' –

Antwort

6

Lassen Sie uns Ihren Code umschreiben als

E1 = (E2 = E3) 

wo E1 ist der Ausdruck a, ist E2 der Ausdruck a += 1 und E3 ist der Ausdruck 10. Hier haben wir festgestellt, dass der Zuweisungsoperator von rechts nach links gruppiert ist (§5.17/1 in C++ 11 Standard).

§5.17/1 Außerdem heißt es:

In allen Fällen ist die Zuordnung nach dem Wert Berechnung der rechten und linken Operanden sequenziert wird und bevor der Wert Berechnung der Zuweisungsausdruck.

Angewandt auf unseren Ausdruck bedeutet, dass wir zuerst die Unterausdrücke E1 und E2 = E3 bewerten müssen. Beachten Sie, dass zwischen diesen beiden Auswertungen keine "sequenziert-vorher" -Beziehung besteht, die jedoch keine Probleme verursacht.

Die Auswertung der ID-expressionE1 trivial ist (das Ergebnis ist a selbst). Die Auswertung des ZuordnungsausdrucksE2 = E3 läuft wie folgt ab:

Zuerst müssen beide Teilausdrücke ausgewertet werden. Die Auswertung des LiteralsE3 ist wiederum trivial (ergibt einen Pr-Wert von 10).

Die Auswertung der (Verbindung) ZuweisungsausdruckE2 in den folgenden Schritten durchgeführt wird:

1) Das Verhalten von a += 1 entspricht a = a + 1a aber wird nur einmal ausgewertet (§5.17/7) . Nach dem Auswerten der Unterausdrücke a und 1 (in einer beliebigen Reihenfolge) wird ein lvalue-to-rvalue Umwandlung auf a konvertiert, um den in a gespeicherten Wert zu lesen.

2) Die Werte von a (die 0) und 1 ist addiert (a + 1) und das Ergebnis dieser Addition ist ein prvalue Wert 1.

3) Bevor wir das Ergebnis der Zuweisung a = a + 1 berechnen können, wird der Wert des Objekts, auf das der linke Operand verweist, durch den Wert des rechten Operanden (§5.17/2) ersetzt. Das Ergebnis von E2 ist dann ein L-Wert, der sich auf den neuen Wert 1 bezieht.Beachten Sie, dass der Nebeneffekt (Aktualisieren des Werts des linken Operanden) vor der Wertberechnung des Zuweisungsausdrucks sequenziert wird. Dies ist § 5.17/1 oben zitiert.

Nachdem wir nun die Unterausdrücke E2 und E3 ausgewertet haben, ist der Wert des Ausdrucks E2 bezieht sich durch den Wert von E3 ersetzt wird, die 10 ist. Daher ist das Ergebnis von E2 = E3 ein Wert von 10.

Schließlich bezieht sich der Ausdruck Wert E1 zu E2 = E3 durch den Wert des Ausdrucks ersetzt wird, die wir 10 berechnet werden. Daher enthält die Variable a den Wert 10.

Da alle diese Schritte gut definiert sind, liefert der gesamte Ausdruck einen genau definierten Wert.

7

Ja, es gab einen Wechsel zwischen C++ 98 und C++ 11. Ich glaube, dass Ihr Beispiel unter C++ 11 Regeln gut definiert ist, während es undefiniertes Verhalten unter C++ 98 Regeln zeigt.

Als ein einfaches Beispiel ist x = ++x; in C++ 98 undefiniert, aber in C++ 11 wohldefiniert. Beachten Sie, dass x = x++; immer noch nicht definiert ist (die Nebenwirkung von Post-Inkrement ist mit der Auswertung des Ausdrucks nicht sequenziert, während die Nebenwirkung von Pre-Inkrement vor dem gleichen sequenziert ist).

+0

Das ist interessant und gut zu wissen, obwohl Sie den Code leider nicht adressieren. Hast du irgendwelche Gedanken darüber, dass es ein undefiniertes/unspezifisches Verhalten ist? – Dariusz

+0

Ich glaube, Ihr Beispiel ist klar definiert. Die Antwort wurde entsprechend aktualisiert. –

3

Nach ein wenig Recherche bin ich überzeugt, dass Ihr Verhalten in C++ 11 gut definiert ist.

$ 1,9/15 Zustände:

Die Wertberechnungen der Operanden eines Operators werden vor Berechnung des Ergebnisses der Bediener den Wert sequenziert.

$ 5,17/1 heißt es:

Der Zuweisungsoperator (=) und die Verbindung Zuweisungsoperator alle Gruppe von rechts nach links.

Wenn ich das richtig verstanden, in Ihrem Beispiel

a = (a+=1) = 10; 

bedeutet dies, dass die Wertberechnungen von (a+=1) und 10 haben, bevor der Wert Berechnung von (a+=1) = 10 gemacht werden und der Wert Berechnung dieser Ausdruck hat fertig sein, bevor a = (a+=1) = 10; ausgewertet wird.

$ 5,17/1 heißt es:

In allen Fällen wird die Zuordnung nach dem Wert Berechnung der rechten und linken Operanden, und bevor der Wert Berechnung des Zuweisungsausdruckes sequenziert.

Dies bedeutet, dass die Zuordnung vor der Wertberechnung passieren muss, und daher wegen Transitivität kann die Auswertung von (a+=1) = 10 erst nach der Zuordnung beginnen a+=1 (Weil ihr Wert erst nach der Nebenwirkung berechnet werden kann).

Das gleiche gilt für die zweite und dritte Aufgabe.

Siehe auch diese excellent answer, die den sequenzierten-vor Beziehung viel detaillierter und viel besser als ich es könnte, erklärt.