Warum ist die Ausgabe des folgenden Programms so, wie sie ist?
#include <iostream>
using namespace std;
int main(){
cout << "2+3 = " <<
cout << 2 + 3 << endl;
}
produziert
2+3 = 15
anstelle der erwarteten
2+3 = 5
Diese Frage hat bereits mehrere Schließ- / Wiedereröffnungszyklen durchlaufen.
Bitte berücksichtigen Sie diese Metadiskussion zu diesem Thema, bevor Sie zum Abschluss abstimmen .
;
am Ende der ersten Ausgabezeile, nicht<<
. Sie drucken nicht das, was Sie zu drucken glauben. Sie tuncout << cout
, was druckt1
(es verwendetcout.operator bool()
, denke ich). Dann folgt5
(von2+3
) sofort und lässt es wie die Nummer fünfzehn aussehen.Antworten:
Ob absichtlich oder versehentlich, Sie haben
<<
am Ende der ersten Ausgabezeile, wo Sie wahrscheinlich gemeint haben;
. Sie haben also im WesentlichenDie Frage läuft also darauf hinaus: Warum
cout << cout;
drucken"1"
?Dies stellt sich vielleicht überraschend subtil heraus.
std::cout
stellt über seine Basisklasse einen bestimmten Typkonvertierungsoperatorstd::basic_ios
bereit , der im booleschen Kontext wie in verwendet werden sollDies ist ein ziemlich schlechtes Beispiel, da es schwierig ist, eine Ausgabe zum Scheitern zu bringen - aber
std::basic_ios
tatsächlich eine Basisklasse für Eingabe- und Ausgabestreams ist und für die Eingabe viel sinnvoller ist:(verlässt die Schleife am Ende des Streams oder wenn Stream-Zeichen keine gültige Ganzzahl bilden).
Die genaue Definition dieses Konvertierungsoperators hat sich zwischen den Versionen C ++ 03 und C ++ 11 des Standards geändert. In älteren Versionen wurde es
operator void*() const;
(normalerweise alsreturn fail() ? NULL : this;
) implementiert , während es in neueren Versionenexplicit operator bool() const;
(normalerweise einfach alsreturn !fail();
) implementiert wurde . Beide Deklarationen funktionieren in einem booleschen Kontext einwandfrei, verhalten sich jedoch unterschiedlich, wenn sie außerhalb eines solchen Kontexts (falsch) verwendet werden.Insbesondere würde nach C ++ 03-Regeln eine Adresse
cout << cout
interpretiertcout << cout.operator void*()
und gedruckt. Nach C ++ 11-Regelncout << cout
sollte überhaupt nicht kompiliert werden, da der Operator deklariert istexplicit
und daher nicht an impliziten Konvertierungen teilnehmen kann. Dies war in der Tat die Hauptmotivation für die Änderung - das Kompilieren von unsinnigem Code zu verhindern. Ein Compiler, der einem der beiden Standards entspricht, würde kein Programm erzeugen, das gedruckt wird"1"
.Anscheinend erlauben bestimmte C ++ - Implementierungen das Mischen und Anpassen des Compilers und der Bibliothek auf eine Weise, die zu einem nicht konformen Ergebnis führt (unter Angabe von @StephanLechner: "Ich habe in xcode eine Einstellung gefunden, die 1 ergibt, und eine andere Einstellung, die eine Adresse ergibt: Sprachdialekt c ++ 98 in Kombination mit "Standardbibliothek libc ++ (LLVM-Standardbibliothek mit C ++ 11-Unterstützung)" ergibt 1, während c ++ 98 in Kombination mit libstdc (gnu c ++ Standardbibliothek) eine Adresse ergibt; "). Sie können einen C ++ 03-Compiler, der
explicit
Konvertierungsoperatoren (die in C ++ 11 neu sind) nicht versteht, mit einer C ++ 11-Bibliothek kombinieren, die die Konvertierung als definiertoperator bool()
. Mit einer solchen Mischung wird es möglichcout << cout
, als interpretiert zu werdencout << cout.operator bool()
, was wiederum einfach istcout << true
und druckt"1"
.quelle
Wie Igor sagt, erhalten Sie dies mit einer C ++ 11-Bibliothek, in
std::basic_ios
der dieoperator bool
anstelle deroperator void*
, aber irgendwie nicht deklariert (oder als) behandelt wirdexplicit
. Sehen Sie hier für die richtige Erklärung.Ein konformer C ++ 11-Compiler liefert beispielsweise das gleiche Ergebnis mit
In Ihrem Fall
static_cast<bool>
wird dies jedoch (fälschlicherweise) als implizite Konvertierung zugelassen.Bearbeiten: Da dies kein gewöhnliches oder erwartetes Verhalten ist, kann es hilfreich sein, Ihre Plattform, Compilerversion usw. zu kennen.
Bearbeiten 2: Als Referenz wird der Code normalerweise entweder als geschrieben
oder als
und es ist das Mischen der beiden Stile, die den Fehler aufgedeckt haben.
quelle
Der Grund für die unerwartete Ausgabe ist ein Tippfehler. Du hast es wahrscheinlich gemeint
Wenn wir die Zeichenfolgen mit der erwarteten Ausgabe ignorieren, bleibt Folgendes übrig:
Seit C ++ 11 ist dies schlecht geformt.
std::cout
ist nicht implizit in etwas konvertierbar, dasstd::basic_ostream<char>::operator<<
(oder eine Überlastung durch Nichtmitglieder) akzeptieren würde. Daher muss ein standardkonformer Compiler Sie zumindest davor warnen. Mein Compiler hat sich geweigert, Ihr Programm zu kompilieren.std::cout
wäre konvertierbar inbool
und die Bool-Überladung des Stream-Eingabeoperators hätte die beobachtete Ausgabe von 1. Diese Überladung ist jedoch explizit und sollte daher keine implizite Konvertierung zulassen. Es scheint, dass Ihre Compiler- / Standardbibliotheksimplementierung nicht genau dem Standard entspricht.In einem Standard vor C ++ 11 ist dies gut formuliert. Damals
std::cout
mußte einen impliziten Umwandlungsoperator anvoid*
dem einen Strom - Eingangsoperator Überlastung hat. Die Ausgabe dafür wäre jedoch unterschiedlich. es würde die Speicheradresse desstd::cout
Objekts drucken .quelle
Der veröffentlichte Code sollte nicht für C ++ 11 (oder einen späteren konformen Compiler) kompiliert werden, er sollte jedoch ohne Warnung bei Implementierungen vor C ++ 11 kompiliert werden.
Der Unterschied besteht darin, dass C ++ 11 die Konvertierung eines Streams in einen Bool explizit vorgenommen hat:
Der ostream-Operator << wird mit einem bool-Parameter definiert. Da eine Konvertierung in Bool existierte (und nicht explizit war), wurde Pre-C ++ 11
cout << cout
übersetzt, incout << true
das 1 ergibt.Und gemäß C.2.15 sollte dies ab C ++ 11 nicht mehr kompiliert werden.
quelle
bool
C ++ 03 gab es keine Konvertierung nach , es gibt jedoch eine,std::basic_ios::operator void*()
die als steuernder Ausdruck einer Bedingung oder Schleife von Bedeutung ist.Auf diese Weise können Sie Ihren Code problemlos debuggen. Wenn Sie
cout
Ihre Ausgabe verwenden, wird sie gepuffert, sodass Sie sie folgendermaßen analysieren können:Stellen Sie sich vor, das erste Auftreten von
cout
repräsentiert den Puffer und der Operator<<
repräsentiert das Anhängen an das Ende des Puffers. Das Ergebnis des Operators<<
ist in Ihrem Fall der Ausgabestreamcout
. Sie starten von:cout << "2+3 = " << cout << 2 + 3 << endl;
Nachdem Sie die oben genannten Regeln angewendet haben, erhalten Sie eine Reihe von Aktionen wie folgt:
buffer.append("2+3 = ").append(cout).append(2 + 3).append(endl);
Wie ich bereits sagte, ist das Ergebnis von
buffer.append()
Puffer. Zu Beginn ist Ihr Puffer leer und Sie müssen die folgende Anweisung verarbeiten:Aussage:
buffer.append("2+3 = ").append(cout).append(2 + 3).append(endl);
Puffer: empty
Zuerst haben Sie,
buffer.append("2+3 = ")
die die angegebene Zeichenfolge direkt in den Puffer legt und wirdbuffer
. Jetzt sieht Ihr Staat so aus:Aussage:
buffer.append(cout).append(2 + 3).append(endl);
Puffer: 2+3 =
Danach analysieren Sie Ihre Aussage weiter und stoßen auf ein
cout
Argument, das an das Ende des Puffers angehängt werden soll. Dascout
wird so behandelt,1
dass Sie1
an das Ende Ihres Puffers anhängen . Jetzt bist du in diesem Zustand:Aussage:
buffer.append(2 + 3).append(endl);
Puffer: 2+3 = 1
Das nächste, was Sie im Puffer haben, ist,
2 + 3
und da das Hinzufügen eine höhere Priorität als der Ausgabeoperator hat, fügen Sie zuerst diese beiden Zahlen hinzu und setzen dann das Ergebnis in den Puffer. Danach erhalten Sie:Aussage:
buffer.append(endl);
Puffer: 2+3 = 15
Schließlich fügen Sie
endl
am Ende des Puffers einen Wert von hinzu und Sie haben:Aussage:
Puffer: 2+3 = 15\n
Nach diesem Vorgang werden die Zeichen aus dem Puffer einzeln aus dem Puffer in die Standardausgabe gedruckt. Das Ergebnis Ihres Codes ist also
2+3 = 15
. Wenn Sie sich das ansehen, erhalten Sie zusätzlich1
voncout
Ihnen versucht zu drucken. Durch Entfernen<< cout
aus Ihrer Anweisung erhalten Sie die gewünschte Ausgabe.quelle
cout << cout
1
auf "Warum produziert man überhaupt ?" , und Sie haben gerade behauptet, dass dies mitten in einer Diskussion über die Verkettung von Einfügeoperatoren der Fall ist.