2009-06-15 13 views
4

Betrachten Sie den folgenden Code ein:C++ Verschiebungs-Operator Vorrang Seltsamkeit

typedef vector<int> intVec; 

intVec& operator<<(intVec& dst, const int i) { 
    dst.push_back(i); 
    return dst; 
} 
int intResult0() { 
    return 23; 
} 
int intResult1() { 
    return 42; 
} 

// main 
intVec v; 
v << intResult0() << intResult1(); 

Das Seltsame ist, dass der Compiler Code erzeugt, der intResult1VORintResult0 (mit neuesten VC und gcc getestet) auswertet. Warum würde der Compiler das tun? Auf diese Weise wird die Zeit zwischen der Auswertung und der Verwendung der jeweiligen Werte (unnötigerweise) erhöht (& thgr;), d. H. 42 wird zuerst abgerufen, aber zuletzt zu dem Vektor geschoben. Dient der C++ - Standard dazu?

+0

heh, ich lief nur in dieser letzten Nacht mit Operator + = T Die verwirrende Sache für mich ist, dass man beim Lesen des Codes erwartet, dass intResult1 als zweiter aufgerufen werden muss, da es den Wert verwendet, der von intResult0 für sein erstes Argument zurückgegeben wird. – Dolphin

Antwort

12

Nach Stroustrup Abschnitt 6.2.2:

Die Reihenfolge der Auswertung von Unterausdrücke innerhalb eines Ausdrucks ist undefiniert.

10

Dies hat nichts mit Vorrang zu tun.

In dieser letzten Anweisung gibt es keinen Sequenzpunkt, so dass der Compiler die Unterausdrücke in beliebiger Reihenfolge bewerten kann, solange die Präzedenz bei der Kombination der Unterausdrücke verwendet wird.

Beachten Sie, dass die Priorität nicht eine allgemeine Reihenfolge der Auswertung definieren - es definiert nur, wie die Operanden eines Ausdrucks mit mehreren Operatoren kombiniert werden.

Zum Beispiel in dem folgenden Ausdruck:

a() * b() + c() 

an einem gewissen Punkt, würden die Compiler müssen (a() * b()) bewerten, bevor sie im Ergebnis c() Zugabe, aber es gibt nichts, das sagt, was jede einzelnen Funktionsaufruf Bedürfnisse bestellen gemacht werden. Der Compiler kann ganz einfach entscheiden, zuerst c() aufzurufen, das Ergebnis auf einen Stack zu schieben und dann alles zu tun, was nötig ist, um den Ausdruck auszuwerten (in diesem Fall könnte er zuerst b() auswerten).

die einzige Rolle, den Vorrang spielt, ist, dass der Compiler nicht gestattet ist, den Ausdruck als zu bewerten:

a() * (b() + c()) 
14

Die Reihenfolge der Auswertung von Unterausdrücken zwischen zwei Sequenzpunkt undefiniert.

Der obige Code ist syntaktischer Zucker für:

v.operator<<(intResult0()).operator<<(intResult1()); 

Die einzige Einschränkung der Compiler hat, ist, dass es alle Parameter auswerten müssen, bevor eine Methode aufgerufen wird und die Vorrangregeln gehorchen. Aber solange diese Regeln eingehalten werden, darf jede Implementierung die Details auswählen und diese Reihenfolge kann sich zwischen den Compilern ändern.

In diesem Beispiel:

  • So ist es vollkommen legal intResult1() vor intResult2() aufrufen.
  • Aber intResult0() muss vor dem Aufruf von Operator < <() (links)
  • und intResult1() aufgerufen werden muss vor dem Aufruf von Operator < <() (rechts)
  • und Betreiber < genannt werden <() (links) muss vor dem Operator aufgerufen werden < <() (rechts)

sehen Sie hier für weitere Informationen:
What are all the common undefined behaviours that a C++ programmer should know about?

und

What are all the common undefined behaviours that a C++ programmer should know about?

2

der C++ Standard, 5: 4

Soweit nicht anders angegeben, die Reihenfolge der Auswertung von Operanden einzelner Operatoren und Teilausdrücke von einzelnen Ausdrücke, und der Reihenfolge , in denen Nebenwirkungen nehmen Platz ist nicht spezifiziert