Ich habe vor Kurzem gesehen, dass ein Stück Code bei comp.lang.c ++ moderiert wurde, indem eine Referenz einer statischen Ganzzahl von einer Funktion zurückgegeben wurde. Der Code war so etwas wie
%Vor% Als ich die Anwendung mit meinem coolen Visual Studio-Debugger debuggte, sah ich nur einen Aufruf zu Aussage A und rate, was mich schockiert hat. Ich dachte immer i+=1
war gleich i=i+1
so
f()+=1
wäre gleich f()=f()+1
und ich würde zwei Aufrufe von f()
sehen, aber ich habe nur einen gesehen. Was zur Hölle ist das? Bin ich verrückt oder ist mein Debugger verrückt geworden oder ist das ein Ergebnis einer vorzeitigen Optimierung?
Das sagt der Standard über +=
und Freunde:
5.17-7: Das Verhalten eines Ausdrucks der Form E1 op = E2 ist äquivalent zu E1 = E1 op E2, außer dass E1 ist nur einmal bewertet. [...]
Damit ist der Compiler richtig.
i+=1
ist funktional das gleiche wie i=i+1
. Es ist tatsächlich anders implementiert (im Grunde ist es entworfen, CPU-Level-Optimierung zu nutzen).
Aber im Wesentlichen wird die linke Seite nur einmal ausgewertet. Es ergibt einen nichtkonstanten l-Wert, was alles ist, um den Wert zu lesen, einen zu addieren und zurückzuschreiben.
Dies wird deutlicher, wenn Sie einen überladenen Operator für einen benutzerdefinierten Typ erstellen. operator+=
ändert die this
-Instanz. operator+
gibt eine neue Instanz zurück. Es wird allgemein empfohlen (in C ++), oop + = first zu schreiben und dann op + zu schreiben.
(Beachten Sie, dass dies nur für C ++ gilt; in C # ist op+=
genau so, wie Sie angenommen haben: nur eine kurze Hand für op+
, und Sie können kein eigenes op + = erstellen. Es wird automatisch für Sie erstellt das Op +)
Ihr Denken ist logisch, aber nicht korrekt.
%Vor% Aber logisch äquivalent und identisch sind nicht gleich.
Der Code sollte so aussehen:
Der Compiler führt zwei Funktionsaufrufe nur durch, wenn Sie zwei Funktionsaufrufe explizit in den Code eingeben. Wenn Sie Nebenwirkungen in Ihren Funktionen haben (wie Sie) und der Compiler begann, extra schwer hinzuzufügen, um implizite Aufrufe zu sehen, wäre es sehr schwierig, den Fluss des Codes wirklich zu verstehen und somit die Wartung sehr schwierig zu machen.
In jeder Sprache, die ich gesehen habe, die einen + = Operator unterstützt, wertet der Compiler den Operanden der linken Seite einmal aus, um einen Typ einer Adresse zu erhalten, der dann sowohl den alten Wert liest als auch den neuen schreibt ein. Der Operator + = ist nicht nur syntaktischer Zucker; Wie Sie bemerken, kann es Ausdruckssemantiken erreichen, die auf andere Weise schwierig zu erreichen wären.
Übrigens haben die "With" -Anweisungen in vb.net und Pascal eine ähnliche Funktion. Eine Aussage wie:
%Vor% berechnet die Adresse von Foo (Bar (Boz)) und setzt dann zwei Felder dieser Struktur auf die Werte neun und zehn. Es wäre äquivalent in C zu %Vor%aber vb.net und Pascal stellen den temporären Zeiger nicht zur Verfügung. Während man in VB.net den gleichen Effekt erreichen kann, ohne "With" zu verwenden, um das Ergebnis von Bar () zu behalten, erlaubt es "With", die temporäre Variable zu vermeiden.
Tags und Links c++ reference premature-optimization