3

Bedenken Sie:Wie erweitert ein C++ - Compiler den Präfix- und Postfixoperator ++()?

class Example 
{ 
private: 
    int m_i; 

public: 
    Example(int i) : m_i{i} {} 

    //Post-fix 
    Example operator++(int) {m_i++; return *this;} 

    //Pre-fix 
    Example& operator++() {m_i++; return *this;} 

    void print() const {std::cout << m_i << '\n'} 
}; 

ich damit experimentiert, um zu bestimmen, wie der Compiler einen Aufruf an die Präfix und Postfix-Operatoren erweitert.

Zum Beispiel, wenn ich so etwas schreiben:

Example e = 1; 
e++; 

ich es erwartet, dass so etwas wie „e.operator ++ (int)“ zu erweitern, oder es einen Schritt weiter, erwartete ich

e++(2); 

zu etwas wie "e.operator ++ (2)." Zu erweitern, aber was ich stattdessen bekomme, ist der Compiler beschweren sich über einige "no match for call to '(Example) (int)'".

Als nächstes war ich neugierig, wie "++ e" auf mysteriöse Weise in "e.operator ++()" expandiert, d. H. Diejenige, die eine Referenz zurückgibt.

um etwas mehr Spielen, landete ich mit:

Example e = 1; 
++e++; 
e.print(); 

Welche 2 gedruckt, und dann:

Example e = 1; 
(++e)++; 
e.print(); 

Welche 3.

gedruckt Ich verstehe, dass (++ e) gibt einen Verweis auf das Objekt zurück, das dann um eins nach oben inkrementiert wird, das macht also Sinn. Ich vermute auch, dass "++ e ++" hier dem Postfixoperator Vorrang einräumt (wie ich in einem anderen Post gelesen habe), so dass die temporäre Variable, die vom Postfixoperator zurückgegeben wird, inkrementiert wird. Auch das macht Sinn. Dies führte mich darüber, wie Ausdrücke zu fragen, wie

++++e 
++e++++ 
++++e++ 
++++e++++ 

werden erweitert (alle kompilieren und ausführen mit den erwarteten Ergebnissen).

Also wirklich, was zum Teufel geht auf der Innenseite und wie weiß der Compiler, welcher Operator ++() aufgerufen wird, und wie werden diese Ausdrücke erweitert (besonders im Präfix Fall)? Was ist der Zweck der Platzhaltervariablen in "operator ++ (int)"?

+2

Der Platzhalter dient nur dazu, die beiden Operatoren während der Überladungsauflösung unterscheiden zu können. Das Argument ist nicht spezifiziert und muss nicht verwendet werden. –

+1

Lesen Sie den obigen Link, alle Ihre konstruierten Beispiele am Ende fehlt Sequenzpunkte. – CoryKramer

+1

Damit sich Ihr Postfix-Operator semantisch wie erwartet verhält, ändern Sie ihn wie folgt: 'Beispieloperator ++ (int) {return Beispiel (m_i ++); } ' –

Antwort

2

Welchen Zweck hat die Platzhaltervariable in "operator ++ (int)"?

Da der Operator ++ zwei verschiedene Funktionen hat: Postfix - ++ und Präfix - ++. Beim Überladen müssen daher zwei verschiedene Funktionssignaturen vorhanden sein.

wie funktioniert der Compiler wissen, welcher Operator ++() aufrufen,

Wenn Ihr Code verwendet präfixkomprimiert ++ (zum Beispiel: ++e;), die Funktion mit Unterschrift operator++() genannt wird. Wenn Ihr Code Postfix-++ (beispielsweise e++;) verwendet, wird die Funktion mit Signatur operator++(int) aufgerufen, und der Compiler wird einen unspezifizierten Dummy-Argument-Wert bereitstellen.

Technisch könnte die Implementierung von operator++(int) den Dummy-Argument-Wert verwenden.Und Sie könnten Ihren eigenen Wert weitergeben, indem Sie e.operator++(5); anstelle von e++; schreiben. Aber dies würde als schlechter Codierungsstil betrachtet werden - wenn man Operatoren überlädt, wird empfohlen, die Semantik der eingebauten Operatoren beizubehalten, um zu vermeiden, dass Leute, die den Code lesen, verwirrt werden.

Beachten Sie, dass Ihre aktuelle Implementierung von postfix- ++ diese Regel nicht beachtet: die normale Semantik ist, dass der vorherige Wert zurückgegeben werden sollte; aber Ihr Code gibt den aktualisierten Wert zurück.

++e++++;

diese Aussage für die Analyse, was Sie über diese Parsing Regeln kennen:

  • Tokens werden von "maximal Munch" analysiert, dh das bedeutet ++ e ++ ++; (und nicht einige einstellige - + Operatoren).
  • Die Sprachgrammatik bestimmt aus diesen Token, welche Ausdrücke Operanden welcher Operatoren sind. Dieser Prozess kann in einer precedence table zusammengefasst werden.

Wenn Sie die Tabelle für diesen Ausdruck konsultieren, erfahren Sie Folgendes: ++(((e++)++)). Mit Hilfe der Erweiterung bereits erwähnt, kann dies in Funktionsaufruf Notation geschrieben werden:

((e.operator++(0)).operator++(0)).operator++(); 

Diese Funktionen aufgerufen werden müssen, von links nach rechts in diesem Fall, da eine Elementfunktion nicht vor dem Ausdruck eingegeben werden kann, wobei es ist aufgerufen wurde ausgewertet.

So Gesetzt wir hatten Example e(1); vor dieser Aussage die folgenden Funktionsaufrufe in dieser Reihenfolge auftreten:

  • e.operator++(int) - setzt e.m_i-2 und gibt eine temporäre (Ich werde es temp1 als Pseudo-Code nennen) mit temp1.m_i als 2.
  • temp1.operator++(int) - setzt temp1.m_i zu 3 und gibt deren temp2m.i3
  • temp2.operator++() ist - setzt temp2.m_i zu 4 und gibt eine Referenz auf temp2.

NB. Meine Antwort spricht nur davon, dass der überladene Operator eine Memberfunktion ist. Es ist auch möglich, ++ (beide Formulare) als Nichtmitglied zu überladen. In diesem Fall wäre das Verhalten gegenüber meiner Beschreibung unverändert, aber der Ausdruck "in Funktionsaufrufnotation geschrieben" würde eine andere Syntax annehmen.

+0

Also was ist mit der Erweiterung von etwas wie "++++ e ++"? Ich nehme an, dass ich mich nicht irrte, wenn ich annahm, dass Postfix Vorrang hat, was bedeuten würde, dass der Compiler etwas (ziemlich grob) äquivalent zu (++ (++ (e ++))) sieht. Ist das richtig? Erweitern Sie dies zu tatsächlichen Funktionsaufrufen, wäre es e.operator ++ (int) .operator ++(). Operator() ++? – AldenB

+0

@AldenB Ja das ist richtig –

+0

Danke, das ist die genaue Art der Antwort, die ich gesucht habe. – AldenB

Verwandte Themen