Welche Auswirkungen haben die in C ++ 17 abgestimmten Garantien für die Evaluierungsreihenfolge (P0145) auf typischen C ++ - Code?
Was ändert sich an Dingen wie den folgenden?
i = 1;
f(i++, i)
und
std::cout << f() << f() << f();
oder
f(g(), h(), j());
c++
c++17
operator-precedence
Johan Lundberg
quelle
quelle
Antworten:
Einige häufige Fälle, in denen die Bewertungsreihenfolge bisher nicht spezifiziert wurde , sind spezifiziert und gültig mit
C++17
. Einige undefinierte Verhaltensweisen sind jetzt nicht mehr spezifiziert.war undefiniert, ist aber jetzt nicht spezifiziert. Insbesondere wird nicht die Reihenfolge angegeben, in der jedes Argument
f
relativ zu den anderen bewertet wird.i++
kann vorher ausgewerteti
werden oder umgekehrt. In der Tat kann ein zweiter Aufruf in einer anderen Reihenfolge ausgewertet werden, obwohl er sich unter demselben Compiler befindet.Die Bewertung jedes Arguments muss jedoch vollständig mit allen Nebenwirkungen ausgeführt werden, bevor ein anderes Argument ausgeführt wird. So erhalten Sie möglicherweise
f(1, 1)
(zweites Argument zuerst ausgewertet) oderf(1, 2)
(erstes Argument zuerst ausgewertet). Aber Sie werden nief(2, 2)
etwas anderes von dieser Art bekommen.wurde nicht angegeben, wird jedoch mit der Priorität des Operators kompatibel, sodass die erste Auswertung von
f
im Stream an erster Stelle steht (Beispiele unten).hat noch eine nicht spezifizierte Bewertungsreihenfolge von g, h und j. Beachten Sie, dass für
getf()(g(),h(),j())
die Regeln der Status angegebengetf()
wird, der zuvor ausgewertet wirdg, h, j
.Beachten Sie auch das folgende Beispiel aus dem Vorschlagstext:
Das Beispiel stammt aus der C ++ - Programmiersprache , 4. Ausgabe, Stroustrup, und war früher ein nicht spezifiziertes Verhalten, aber mit C ++ 17 funktioniert es wie erwartet. Es gab ähnliche Probleme mit wiederaufnehmbaren Funktionen (
.then( . . . )
).Betrachten Sie als weiteres Beispiel Folgendes:
Mit C ++ 14 und früher können (und werden) wir Ergebnisse erhalten wie
anstatt
Beachten Sie, dass das oben Gesagte tatsächlich dasselbe ist wie
Vor C ++ 17 gab es jedoch keine Garantie dafür, dass die ersten Aufrufe zuerst in den Stream gelangen.
Referenzen: Aus dem angenommenen Vorschlag :
Anmerkung bearbeiten: Meine ursprüngliche Antwort wurde falsch interpretiert
a(b1, b2, b3)
. Die Reihenfolge derb1
,b2
,b3
ist noch nicht spezifiziert. (Danke @KABoissonneault, alle Kommentatoren.)Jedoch (wie @Yakk weist darauf hin) , und das ist wichtig: Auch wenn
b1
,b2
,b3
sind nicht-triviale Ausdrücke ist jeder von ihnen vollständig ausgewertet und an die jeweiligen Funktionsparameter gebunden , bevor die andere ausgewertet wird gestartet. Der Standard besagt dies folgendermaßen:Einer dieser neuen Sätze fehlt jedoch im GitHub-Entwurf :
Das Beispiel ist da. Es löst ein jahrzehntealtes Problem ( wie von Herb Sutter erklärt ) mit Ausnahme der Sicherheit, wo Dinge wie
würde auslaufen, wenn einer der Aufrufe
get_raw_a()
ausgelöst würde, bevor der andere Rohzeiger an seinen Smart-Pointer-Parameter gebunden wurde.Wie von TC hervorgehoben, ist das Beispiel fehlerhaft, da die Konstruktion von unique_ptr aus dem Rohzeiger explizit ist und dies das Kompilieren verhindert. *
Beachten Sie auch diese klassische Frage (markiert mit C , nicht mit C ++ ):
ist noch undefiniert.
quelle
a
, dannb
, dannc
, dannd
" und dann zeigta(b1, b2, b3)
, was darauf hindeutet, dass nicht alleb
Ausdrücke notwendigerweise in irgendeiner Reihenfolge ausgewertet werden (andernfalls wäre esa(b, c, d)
)a(b1()(), b2()())
bestellen kannb1()()
undb2()()
in beliebiger Reihenfolge, aber es kann nicht tun ,b1()
dannb2()()
dannb1()()
: es nicht mehr kann Verschachtelung ihrer Ausführungen. Kurz gesagt, "8. ALTERNATE EVALUATION ORDER FOR FUNCTION CALLS" war Teil der genehmigten Änderung.f(i++, i)
war undefiniert. Es ist jetzt nicht spezifiziert. Stroustrups String-Beispiel war wahrscheinlich nicht spezifiziert, nicht undefiniert. `f (get_raw_a (), get_raw_a ());` wird nicht kompiliert, da der relevanteunique_ptr
Konstruktor explizit ist. Schließlichx++ + ++x
ist undefiniert, Punkt.Interleaving ist in C ++ 17 verboten
In C ++ 14 war Folgendes unsicher:
Während des Funktionsaufrufs finden hier vier Operationen statt
new A
unique_ptr<A>
Konstrukteurnew B
unique_ptr<B>
KonstrukteurDie Reihenfolge dieser war völlig unbestimmt, und daher ist eine vollkommen gültige Reihenfolge (1), (3), (2), (4). Wenn diese Reihenfolge ausgewählt wurde und (3) ausgelöst wird, ist der Speicher von (1) undicht - wir haben (2) noch nicht ausgeführt, was das Leck verhindert hätte.
In C ++ 17 verbieten die neuen Regeln das Interleaving. Aus [intro.execution]:
Zu diesem Satz gibt es eine Fußnote, die lautet:
Dies lässt uns zwei gültige Ordnungen übrig: (1), (2), (3), (4) oder (3), (4), (1), (2). Es ist nicht spezifiziert, welche Bestellung angenommen wird, aber beide sind sicher. Alle Bestellungen, bei denen (1) (3) beide vor (2) und (4) erfolgen, sind jetzt verboten.
quelle
Ich habe einige Hinweise zur Reihenfolge der Ausdrucksbewertung gefunden:
In P0145R3.Refining Expression Evaluation Order für Idiomatic C ++ habe ich gefunden:
Aber ich habe es nicht im Standard gefunden, sondern im Standard:
Also habe ich das Verhalten in drei Compilern für 14 und 17 Standards verglichen. Der untersuchte Code lautet:
Ergebnisse (desto konsistenter ist das Klirren):
quelle