Warum werden bitweise Verschiebungen (<< und >>) für cout und cin verwendet?

76

Frage ist wirklich im Titel; Ich bin mir sicher, dass es etwas Logisches gibt, aber jetzt bin ich ratlos!

Crowstar
quelle
10
Ich denke, das liegt daran, dass sie Pfeilen ähneln, die auf den Fluss einer Substanz hinweisen.
Pointy
1
Nur raten, aber ich stelle mir vor, es liegt daran, dass Sie Daten von oder in eine Datei "verschieben".
Mark Ransom
8
Der Vollständigkeit halber: Diese werden in diesem Zusammenhang als Einfügeoperatoren bezeichnet
ChristopheD
6
@Pointy: wie wäre es mit Funktionen wie read()und write()? Ich denke, benutzerdefinierte Operatoren sollten eine ähnliche Semantik haben wie die integrierten Operatoren, z. B. +um komplexe Zahlen oder geometrische Vektoren hinzuzufügen. Hat ostream::operator<<aber nichts mit Bitverschiebung zu tun. Einige der frühen C ++ - Entwurfsentscheidungen werden jetzt als problematisch angesehen, z. B. die automatische Generierung von Kopierkonstruktoren, wenn ein Destruktor vorhanden ist, sodass die Auswahl nicht unbedingt logisch sein muss operator<<.
Philipp
1
@ Crowstar: Darf ich die Frage umkehren? Warum werden Einfüge- und Extraktionsoperatoren für die bitweise Verschiebung verwendet? Persönlich benutze ich Streams häufiger als bitweise Manipulationen;)
Matthieu M.

Antworten:

67

Gemäß §8.3.1 von Design und Evolution von C ++ :

Die Idee, einen Ausgabeoperator anstelle einer benannten Ausgabefunktion bereitzustellen, wurde von Doug McIlroy in Analogie zu den E / A-Umleitungsoperatoren in der UNIX-Shell (>, >>, | usw.) vorgeschlagen.

[...]

Für Eingabe- und Ausgabeoperationen wurden mehrere Operatoren berücksichtigt: Der Zuweisungsoperator war sowohl für die Eingabe als auch für die Ausgabe ein Kandidat, bindet jedoch falsch. Das cout=a=bwürde so interpretiert werden cout=(a=b), und die meisten Leute schienen es vorzuziehen, dass sich der Eingabeoperator vom Ausgabeoperator unterscheidet. Die Operatoren <und >wurden ausprobiert, aber die Bedeutungen "kleiner als" und "größer als" waren so fest in den Köpfen der Menschen verankert, dass die neuen E / A-Anweisungen für alle praktischen Zwecke unlesbar waren (dies scheint für <<und nicht der Fall zu sein >>). . Abgesehen davon steht '<' auf den meisten Tastaturen direkt über ', und die Leute haben Ausdrücke wie diesen geschrieben:

cout < x , y, z;

Es ist nicht einfach, dafür gute Fehlermeldungen zu geben.

Jerry Sarg
quelle
18

Vielleicht, weil es dem Unix-Append-Vorgang ähnlich sieht, da Sie im Wesentlichen an einen Eingabe- / Ausgabestream anhängen?

Z.B

Ausgabe

echo "foo" >> bar

Eingang

sendmail -f [email protected] << myemail.txt

(Gestohlenes Eingabebeispiel von Zac Howland)

Abe Voelker
quelle
1
@ Federico: Eigentlich schon. Sie verwenden <<in der UNIX-Befehlszeile Einfügungen : sendmail -f [email protected] << myemail.txt.
Zac Howland
@Zac danke für das Beispiel; Ich habe es der Antwort hinzugefügt, um es vollständiger zu machen
Abe Voelker
1
Dies ist meine bevorzugte Erklärung. Ich hasse es einfach, wie C ++ befürwortet, die Bedeutung von Operatoren zu ändern. Ich versuche, mich von JEDER Bibliothek oder jedem Code fernzuhalten, der diese Bedeutung ändert. IE MyWebRequest = " google.com "; tatsächlich das Herunterladen der Webseite durch eine Aufgabe. Und obwohl ich die Notwendigkeit dafür verstehe, denke ich, dass die Überlastung des Bedieners viel zu sehr missbraucht wird. Ich hätte es sehr bevorzugt, wenn sie so etwas wie <- oder -> verwendet hätten, um die Bedeutung nicht zu ändern ODER den Bitverschiebungsoperator in etwas anderes zu ändern.
Rahly
@rahly: Aber das Token hat ->bereits eine bestehende Bedeutung, ebenso wie das Token-Paar<-
Ben Voigt
Sie haben Recht ... aber es ging mir nicht darum, die Bedeutung bestehender Operatoren zu ändern .... vielleicht:> und <: .... auto tmp = myvar << 12; hat keine wirkliche Bedeutung
Rahly
11

Aus "Die C ++ - Programmiersprache". Stroustrups (Sprachautoren) Wörter:

Das Überladen des Operators <<mit "put to" ergibt eine bessere Notation und ermöglicht es dem Programmierer, eine Folge von Objekten in einer einzigen Anweisung auszugeben.

Aber warum <<? Es ist nicht möglich, ein neues lexikalisches Token zu erfinden. Der Zuweisungsoperator war ein Kandidat für Eingabe und Ausgabe, aber die meisten Leute schienen es vorzuziehen, unterschiedliche Operatoren für Eingabe und Ausgabe zu verwenden. Außerdem bindet = den falschen Weg; das heißt, cout = a = b bedeutet cout = (a = b) und nicht (cout = a) = b. Ich habe die Operatoren ausprobiert <und >, aber die Mittelwerte "kleiner als" und "größer als" waren so fest in den Köpfen der Menschen verankert, dass die neuen E / A-Anweisungen für alle praktischen Zwecke unlesbar waren.

UmmaGumma
quelle
7

>>und <<sind nur Operatoren und Sie können Ihre eigenen >>und <<für Ihre Klassen implementieren .

Ich nehme an, "jemand" hat sie ausgewählt, weil: a) sie Shell-Dateivorgängen ähnlich sind und b) vorhandene Operatoren wiederverwendet werden, weil keine neuen erstellt werden müssen

Elalfer
quelle
6

Sie erinnern sich also daran, dass das, was Sie eingeben, in die Variable einfließt, wenn Sie cinals Tastatur und coutals Monitor denken

cin>>var;

Oder der Inhalt Ihrer Variablen wird in Richtung Bildschirm angezeigt

cout<<var;
Federico klez Culloca
quelle
6

Weil sie mehr oder weniger einen vernünftigen Vorrang hatten und gut aussahen. In C ++ können Sie keine neuen Operatoren erstellen oder deren Vorrang- oder Gruppierungsregeln ändern. Sie können nur vorhandene Operatoren überladen und ihre tatsächlichen Funktionen ändern.

Die Wahl von <<und >>hat einige unglückliche Nebenwirkungen, weil es irgendwie die Idee vorantreibt, dass die Ausgabe unter Einhaltung der Reihenfolge erfolgt. Während dies dank eines cleveren Verkettungstricks für die tatsächliche Ausgabe gilt, ist es für die beteiligten Berechnungen falsch, und dies ist sehr oft überraschend.

Genauer gesagt

std::cout << foo() << bar() << std::eol;

bedeutet NICHT, dass foodas vorher aufgerufen wird bar.

BEARBEITEN

Mit C ++ 17 wurde das Sequenzproblem "behoben". Nun ist die Reihenfolge der Auswertung angegeben werden von links nach rechts für <<und >>Betreiber. Es gibt immer noch Stellen in C ++, an denen die Reihenfolge der Bewertung nicht angegeben ist (oder sogar nicht vorhanden ist, was bedeutet, dass die Bewertung verschachtelt werden kann), aber einige häufige Fälle verhalten sich jetzt vorhersehbar und portabel. Siehe diese Antwort .

6502
quelle
Aber wen kümmert's? Wenn Sie Funktionen mit Nebenwirkungen in einer Ausgabeanweisung verwenden, erstellen Sie ohnehin unlesbaren und nicht wartbaren Code. (Ansonsten gilt das Argument natürlich auch für viele andere Fälle. Und es gibt ein gutes Argument für die Auferlegung der Reihenfolge - Fehler reproduzierbar zu machen, wenn Sie einen Fehler machen und einen Nebeneffekt bekommen.)
James Kanze
1
@JamesKanze: Ich habe einfach festgestellt, dass viele C ++ - Programmierer der Meinung sind, dass Code im Beispiel foo()garantiert vorher aufgerufen wird bar()... und sie schreiben Code so, wie er einige s << header() << body() << footer();in body()berechnete Summen berechnet footer(). Diese Art von Fehler tritt stattdessen seltener bei Funktionsparametern auf.
6502
Ich würde keinen Programmierer einstellen, der solchen Code geschrieben hat, selbst wenn die Bestellung garantiert wäre. Solche versteckten Abhängigkeiten sind ein wahrer Alptraum für die Wartung.
James Kanze
3
@ JamesKanze: Dieser Kommentar klingt komisch (bitte denken Sie daran, dass Sie über eine Bibliothek mit Horror wie setwund setfilldarin sprechen ).
6502
4

Sie sind keine bitweisen Operatoren. Sie werden in diesem Zusammenhang als Einfüge- und Extraktionsoperatoren bezeichnet.

http://www.cplusplus.com/doc/tutorial/basic_io/

Diese werden nur zur visuellen Interpretation verwendet. Wenn Sie sich mit der Entwicklung einer eigenen Stream- und Operatorüberladung befassen, können Sie sehen, dass Sie sogar + für die Eingabe und - für die Ausgabe verwenden können :)

Sarwar Erfan
quelle
4

Vor allem wegen ihrer Assoziativität. Die Einfüge- und Extraktionsoperatoren werden also von links nach rechts zugeordnet

std::cout << "Hello" << ' ' << 4 << 2;

bewertet wie erwartet: zuerst mit "Hello", dann mit ' 'und schließlich mit 4und 2. Zugegeben, der Additionsoperator operator+assoziiert auch von links nach rechts. Aber dieser Operator und andere mit Links-Rechts-Assoziativität haben bereits eine andere Bedeutung.

wilhelmtell
quelle
3

Diese Antwort ist unbefriedigend, aber richtig: Sie sind keine bitweisen Operatoren.

Die Bedeutung des Operators wird durch den links angezeigten Datentyp bestimmt. Bei cin und cout (und anderen Stream-Typen) verschieben die Operatoren << und >> Werte zu und von Streams. Für den Fall, dass der linke Operand eine Ganzzahl ist, ist die Operation die bitweise Operation, die Sie bereits aus C kennen.

Die Bedeutung des Operators ist nicht festgelegt, obwohl er Vorrang hat.

Kevin A. Naudé
quelle
1

Bjarne wählte sie aufgrund ihres praktischen Vorrangs, ihrer Assoziativität und ihres Gedächtniswerts.

Die Priorität ist nicht perfekt, z. B. sind die Operatoren auf Boolescher Ebene und auf Bitebene problematisch.

Aber es ist ziemlich in Ordnung.

Prost und hth. - Alf
quelle
1

Einfügeoperator >>und <<werden mit dem Eingabestream bzw. dem Ausgabestream verwendet, da der Eingabestream den Datenfluss in Ihr Programm und der Ausgabestream den Datenfluss aus Ihrem Programm bedeutet. Da diese Einfügeoperatoren wie Richtungsoperatoren (Anzeigen der Datenflussrichtung) aussehen, werden >>sie für den Eingabestream und <<den Ausgabestream ausgewählt.

Schauen Sie sich den Teil des Codes an ...

int Num1;
cin >> Num1;

Wenn Sie dies genau beobachten, >>wird der Datenfluss zur Variablen (im Programm deklariert) angezeigt. Dies bedeutet den Datenfluss zum Programm, der eine Aufgabe des Eingabestreams ist (hier cin).

ähnlich geht mit cout,

int Num2 = 5;
cout << Num2;

Hier wird <<der Datenfluss aus dem Programm (als Num2Teil des Programms) angezeigt, der die Aufgabe des Ausgabestreams ist.

Ich hoffe, das alles ergibt für Sie einen Sinn.

Gopal Sharma
quelle
1
Hallo! Willkommen bei StackOverflow. Ich habe gerade eine Bearbeitung eingereicht, in der Ihr Code markiert ist (er wird überprüft). Sie können es mit der {}Schaltfläche oder mit vier Leerzeichen einrücken. Sie können Inline-Code auch mit Backticks (`) markieren.
Rrauenza
0
cout << "Output sentence"; // prints Output sentence on screen
cout << 120;               // prints number 120 on screen
cout << x;                 // prints the content of x on screen 

Der Operator << fügt die darauf folgenden Daten in den Stream vor ihm ein. In den obigen Beispielen wurden der Konstantstring-Ausgabesatz, die numerische Konstante 120 und die Variable x in den Standardausgabestream cout eingefügt.

Das Standardeingabegerät ist normalerweise die Tastatur. Die Verarbeitung der Standardeingabe in C ++ erfolgt durch Anwenden des überladenen Extraktionsoperators (>>) auf den Cin-Stream. Dem Operator muss die Variable folgen, die die Daten speichert, die aus dem Stream extrahiert werden sollen. Zum Beispiel:

int age;
cin >> age;
Ayush
quelle
0

Ich gehe davon aus, dass Sie wissen, dass C ++ das Überladen von Operatoren ermöglicht. Im Allgemeinen überladen Sie Operatoren nur, wenn die Semantik vollständig übertragbar ist (z. B. Überladen der Addition für eine Vektorklasse, um zwei Vektoren zu addieren). Ich denke, Ihre Frage bezieht sich darauf, warum man Bitshift-Operatoren verwenden, sie für den Iostream überladen und ihnen eine völlig andere Bedeutung als ihren ursprünglichen Zweck geben würde. Der Grund dafür ist, dass Bitshift-Operationen so weit von dem entfernt sind, was Iostreams tun, dass niemand verwirrt werden könnte, wenn er denkt, dass << oder >> eine Bitshift für einen Iostream ausführt. Und der Grund, warum sie bequem zu verwenden sind, ist auch, dass ihre Reihenfolge darin besteht, zuerst den Operanden links, dann den rechts zu bewerten und die Operation auszuführen.

Aber zur ursprünglichen Frage, warum? Ich weiß es nicht wirklich, es scheint mir nur so, als ob << und >> ziemlich leicht zu verstehen sind, Informationen von einer Entität zu nehmen und in die andere zu setzen. Warum muss der Grund komplizierter sein? Es erscheint sinnvoll, diese zu verwenden, da ihre Bedeutung offensichtlich ist. Was können Sie besser von einem Operator verlangen?

Mikael Persson
quelle
4
-1: Die Reihenfolge der Auswertung der linken und rechten Seite von <<und >>ist nicht garantiert. Dies ist in der Tat manchmal eine Quelle von Fehlern, wenn Leute Dinge schreiben s << foo() << bar()und erwarten foo, dass sie vorher angerufen werden bar. Was garantiert ist, ist, dass das Ergebnis von foovor dem Ergebnis von an den Stream gesendet wird, baraber die Berechnungsreihenfolge NICHT garantiert ist. Nur ,, ||und &&binäre Operatoren geben eine solche Garantie ... und das Garantie sowieso ist nur vorhanden , wenn Sie nicht überlasten sie nicht.
6502