
Pravs
Kürzlich in einem Interview gab es eine folgende objektive Artfrage.
int a = 0;
cout << a++ << a;
Antworten:
A. 10
B. 01
C. undefiniertes Verhalten
Ich habe Wahl b beantwortet, dh Ausgabe wäre “01”.
Aber zu meiner Überraschung wurde mir später von einem Interviewer gesagt, dass die richtige Antwort Option c ist: undefiniert.
Nun, ich kenne das Konzept der Sequenzpunkte in C++. Das Verhalten ist für die folgende Anweisung nicht definiert:
int i = 0;
i += i++ + i++;
aber nach meinem Verständnis für die Aussage cout << a++ << a
der ostream.operator<<()
würde zweimal angerufen werden, zuerst mit ostream.operator<<(a++)
und später ostream.operator<<(a)
.
Ich habe auch das Ergebnis auf dem VS2010-Compiler überprüft und seine Ausgabe ist ebenfalls ’01’.

Maxim Egorushkin
Sie können sich vorstellen:
cout << a++ << a;
Als:
std::operator<<(std::operator<<(std::cout, a++), a);
C++ garantiert, dass alle Seiteneffekte früherer Auswertungen durchgeführt wurden Sequenzpunkte. Es gibt keine Sequenzpunkte zwischen der Auswertung der Funktionsargumente, was dieses Argument bedeutet a
kann vor dem Argument ausgewertet werden std::operator<<(std::cout, a++)
oder danach. Das Ergebnis des Obigen ist also undefiniert.
C++17-Update
In C++17 wurden die Regeln aktualisiert. Bestimmtes:
In einem Verschiebungsoperatorausdruck E1<<E2
und E1>>E2
jede Wertberechnung und Nebenwirkung von E1
wird vor jeder Wertberechnung und Nebenwirkung von sequenziert E2
.
Das bedeutet, dass der Code erforderlich ist, um ein Ergebnis zu erzeugen b
die ausgibt 01
.
Sehen P0145R3 Verfeinern der Ausdrucksauswertungsreihenfolge für idiomatisches C++ für mehr Details.

Alok Speichern
Technisch gesehen ist dies insgesamt Undefiniertes Verhalten.
Aber es gibt zwei wichtige Aspekte bei der Antwort.
Die Code-Anweisung:
std::cout << a++ << a;
wird bewertet als:
std::operator<<(std::operator<<(std::cout, a++), a);
Der Standard definiert nicht die Reihenfolge der Auswertung von Argumenten für eine Funktion.
Also entweder:
std::operator<<(std::cout, a++)
wird zuerst ausgewertet bzw
a
wird zuerst ausgewertet bzw
- es kann sich um eine beliebige implementierungsdefinierte Reihenfolge handeln.
Diese Reihenfolge ist Nicht spezifiziert[Ref 1] nach Norm.
[Ref 1]C++03 5.2.2 Funktionsaufruf
Abs. 8
Die Reihenfolge der Bewertung der Argumente ist nicht festgelegt. Alle Seiteneffekte der Auswertung von Argumentausdrücken werden wirksam, bevor die Funktion eingegeben wird. Die Reihenfolge der Auswertung des Postfix-Ausdrucks und der Argumentausdrucksliste ist nicht festgelegt.
Ferner gibt es keinen Sequenzpunkt zwischen der Auswertung von Argumenten für eine Funktion, sondern ein Sequenzpunkt existiert nur nach der Auswertung aller Argumente[Ref 2].
[Ref 2]C++03 1.9 Programmausführung [intro.execution]:
Abs. 17:
Beim Aufruf einer Funktion (unabhängig davon, ob es sich um eine Inline-Funktion handelt oder nicht) gibt es nach der Auswertung aller Funktionsargumente (falls vorhanden) einen Sequenzpunkt, der vor der Ausführung von Ausdrücken oder Anweisungen im Funktionsrumpf stattfindet.
Beachten Sie, dass hier der Wert von c
mehr als einmal ohne dazwischenliegenden Sequenzpunkt zugegriffen wird, heißt es dazu in der Norm:
[Ref 3]C++03 5 Ausdrücke [expr]:
Absatz 4:
….
Zwischen dem vorherigen und dem nächsten Sequenzpunkt darf ein Skalarobjekt seinen gespeicherten Wert höchstens einmal durch die Auswertung eines Ausdrucks modifizieren. Außerdem soll auf den vorherigen Wert nur zugegriffen werden, um den zu speichernden Wert zu bestimmen. Die Anforderungen dieses Absatzes müssen für jede zulässige Reihenfolge der Teilausdrücke eines vollständigen Ausdrucks erfüllt werden; andernfalls ist das Verhalten undefiniert.
Der Code ändert sich c
mehr als einmal ohne dazwischenliegenden Sequenzpunkt und es wird nicht darauf zugegriffen, um den Wert des gespeicherten Objekts zu bestimmen. Dies ist ein klarer Verstoß gegen die obige Klausel und daher das von der Norm vorgeschriebene Ergebnis Undefiniertes Verhalten[Ref 3].

James Kanze
Sequenzpunkte definieren nur a teilweise Bestellung. In Ihrem Fall haben Sie (sobald die Überladungsauflösung abgeschlossen ist):
std::cout.operator<<( a++ ).operator<<( a );
Es gibt einen Sequenzpunkt zwischen den a++
und der erste Anruf zu
std::ostream::operator<<
und es gibt einen Sequenzpunkt zwischen dem zweiten a
und der zweite Aufruf an std::ostream::operator<<
aber es gibt keinen Sequenzpunkt dazwischen a++
und a
; die einzigen Bestellbeschränkungen sind die a++
vor dem ersten Anruf vollständig ausgewertet werden (einschließlich Nebenwirkungen). operator<<
und das die zweite a
vor dem zweiten Aufruf vollständig ausgewertet werden operator<<
. (Es gibt auch kausale Ordnungseinschränkungen: der zweite Aufruf von to operator<<
kann dem ersten nicht vorangestellt werden, da es die Ergebnisse des ersten als Argument benötigt.) §5/4 (C++03) besagt:
Sofern nicht anders angegeben, ist die Reihenfolge der Auswertung von Operanden einzelner Operatoren und Unterausdrücke einzelner Ausdrücke sowie die Reihenfolge, in der Seiteneffekte auftreten, nicht festgelegt. Zwischen dem vorherigen und dem nächsten Sequenzpunkt darf ein Skalarobjekt seinen gespeicherten Wert höchstens einmal durch die Auswertung eines Ausdrucks modifizieren. Außerdem soll auf den vorherigen Wert nur zugegriffen werden, um den zu speichernden Wert zu bestimmen. Die Anforderungen dieses Absatzes müssen für jede zulässige Reihenfolge der Teilausdrücke eines vollständigen Ausdrucks erfüllt werden; andernfalls ist das Verhalten undefiniert.
Eine der zulässigen Reihenfolgen Ihres Ausdrucks ist a++
, a
erster Aufruf an operator<<
zweiter Aufruf an operator<<
; dies modifiziert den gespeicherten Wert von a
(a++
) und greift darauf zu, außer um den neuen Wert zu bestimmen (die zweite a
), ist das Verhalten undefiniert.
Die richtige Antwort ist, die Frage zu hinterfragen. Die Aussage ist nicht akzeptabel, weil ein Leser keine klare Antwort sehen kann. Eine andere Betrachtungsweise ist, dass wir Nebeneffekte (c++) eingeführt haben, die die Interpretation der Anweisung erheblich erschweren. Kurzer Code ist großartig, vorausgesetzt, seine Bedeutung ist klar.
9877100cookie-checkWas ist die richtige Antwort für coutyes
Hast du nach einer Erklärung gefragt? Ich interviewe oft potenzielle Kandidaten und bin sehr daran interessiert, Fragen zu erhalten, das zeigt Interesse.
– Brady
28. Mai 2012 um 10:14 Uhr
@jrok Es ist undefiniertes Verhalten. Alles, was die Implementierung tut (einschließlich des Versendens einer beleidigenden E-Mail in Ihrem Namen an Ihren Chef), ist konform.
– James Kanze
28. Mai 2012 um 11:12 Uhr
Diese Frage schreit nach einem C++11 (the Strom Version von C++) Antwort, die keine Sequenzpunkte erwähnt. Leider kenne ich mich mit dem Ersatz von Sequenzpunkten in C++11 nicht aus.
– CB Bailey
28. Mai 2012 um 11:34 Uhr
Wenn es nicht undefiniert wäre, könnte es definitiv nicht sein
10
es wäre entweder01
oder00
. (c++
wird immer den Wert auswertenc
hatte Vor inkrementiert wird). Und selbst wenn es nicht undefiniert wäre, wäre es immer noch schrecklich verwirrend.– linksherum
28. Mai 2012 um 11:38 Uhr
Weißt du, als ich den Titel „cout << c++ << c“ las, dachte ich kurz daran als eine Aussage über die Beziehung zwischen den Sprachen C und C++ und einer anderen namens „cout“. Weißt du, wie jemand gesagt hat, dass er dachte, dass „cout“ C++ viel unterlegen sei und dass C++ C viel unterlegen sei – und wahrscheinlich aufgrund der Transitivität, dass „cout“ sehr, sehr viel schlechter als C. 🙂
– tchrist
28. Mai 2012 um 19:36 Uhr