Ich denke, dieser Teil des Standardentwurfs bezüglich der Reihenfolge der Bewertung ist relevant:
1.9 Programmausführung
...
- Sofern nicht anders angegeben, werden Auswertungen von Operanden einzelner Operatoren und von Unterausdrücken einzelner Ausdrücke nicht sequenziert. Die Wertberechnungen der Operanden eines Operators werden vor der Wertberechnung des Ergebnisses des Operators sequenziert. Wenn eine Nebenwirkung auf ein Skalarobjekt in Bezug auf eine andere Nebenwirkung auf dasselbe Skalarobjekt oder eine Wertberechnung unter Verwendung des Werts desselben Skalarobjekts nicht sequenziert wird und sie möglicherweise nicht gleichzeitig auftreten, ist das Verhalten undefiniert
und auch:
5.2.2 Funktionsaufruf
...
- [Hinweis: Die Auswertungen des Postfix-Ausdrucks und der Argumente sind relativ zueinander nicht sequenziert. Alle Nebenwirkungen von Argumentauswertungen werden vor Eingabe der Funktion sequenziert - Endnote]
Überlegen Sie sich also für Ihre Zeile c.meth1(&nu).meth2(nu);
, was im Operator in Bezug auf den Funktionsaufrufoperator für den letzten Aufruf von geschieht meth2
, damit wir die Aufteilung in den Postfix-Ausdruck und das Postfix-Argument deutlich sehen nu
:
operator()(c.meth1(&nu).meth2, nu);
Die Auswertungen des Postfix-Ausdrucks und des Arguments für den endgültigen Funktionsaufruf (dh des Postfix-Ausdrucks c.meth1(&nu).meth2
und nu
) sind gemäß der obigen Funktionsaufrufregel relativ zueinander nicht sequenziert . Daher ist der Nebeneffekt der Berechnung des Postfix-Ausdrucks auf das ar
Skalarobjekt relativ zur Argumentauswertung von nu
vor dem meth2
Funktionsaufruf nicht sequenziert . Nach der obigen Programmausführungsregel ist dies ein undefiniertes Verhalten.
Mit anderen Worten, der Compiler muss das nu
Argument für den meth2
Aufruf nach dem meth1
Aufruf nicht auswerten. Es ist frei, keine Nebenwirkungen anzunehmen, die sich meth1
auf die nu
Auswertung auswirken .
Der oben erzeugte Assembler-Code enthält die folgende Sequenz in der main
Funktion:
- Die Variable
nu
wird auf dem Stapel zugewiesen und mit 0 initialisiert.
- Ein Register (
ebx
in meinem Fall) erhält eine Kopie des Wertes vonnu
- Die Adressen von
nu
und c
werden in Parameterregister geladen
meth1
wird genannt
- Das Rückgabewert - Register und der zuvor zwischengespeicherte Wert des
nu
in dem ebx
Register werden in Parameterregister geladen
meth2
wird genannt
Entscheidend ist, dass der Compiler in Schritt 5 oben zulässt, dass der zwischengespeicherte Wert nu
von Schritt 2 im Funktionsaufruf an wiederverwendet wird meth2
. Hier wird die Möglichkeit außer Acht gelassen, die nu
durch den Aufruf von meth1
"undefiniertes Verhalten" in Aktion geändert wurde .
HINWEIS: Diese Antwort hat sich im Wesentlichen von ihrer ursprünglichen Form geändert. Meine anfängliche Erklärung in Bezug auf die Nebenwirkungen der Operandenberechnung, die nicht vor dem letzten Funktionsaufruf sequenziert wurden, war falsch, weil sie es sind. Das Problem ist die Tatsache, dass die Berechnung der Operanden selbst unbestimmt sequenziert ist.