Warum gibt string :: compare ein int zurück?

102

Warum gibt string::compareein intstatt eines kleineren Typs wie shortoder zurück ?char ? Nach meinem Verständnis gibt diese Methode nur -1, 0 oder 1 zurück.

Zweiter Teil, wenn ich eine Vergleichsmethode entwerfen wollte, die zwei Objekte vom Typ vergleicht Foo und nur -1, 0 oder 1 zurückgeben möchte, wäre die Verwendung shortoder im charAllgemeinen eine gute Idee?

BEARBEITEN: Ich wurde korrigiert, string::comparegibt nicht -1, 0 oder 1 zurück, sondern gibt einen Wert> 0, <0 oder 0 zurück. Danke, dass Sie mich auf dem Laufenden gehalten haben.

Die Antwort scheint grob zu sein. Es gibt keinen Grund, einen Typ zurückzugeben, der kleiner als ist int weil Rückgabewerte "rvalues" sind und diese "rvalues" nicht davon profitieren, kleiner als type int (4 Bytes) zu sein. Viele Leute wiesen auch darauf hin, dass die Register der meisten Systeme wahrscheinlich von Größe sein werdenint sowieso die werden, da diese Register gefüllt werden, unabhängig davon, ob Sie ihnen einen Wert von 1, 2 oder 4 Byte geben. Es gibt keinen wirklichen Vorteil, a zurückzugeben kleinerer Wert.

BEARBEITEN 2: Tatsächlich sieht es so aus, als ob bei Verwendung kleinerer Datentypen wie Ausrichtung, Maskierung usw. ein zusätzlicher Verarbeitungsaufwand anfällt. Der allgemeine Konsens ist, dass die kleineren Datentypen vorhanden sind, um Speicherplatz zu sparen, wenn mit vielen Daten gearbeitet wird, wie im Fall eines Arrays.

Habe heute etwas gelernt, nochmals vielen Dank Jungs!

Cody Smith
quelle
Ich denke, was wäre besser, wenn es einen spezifischeren Typ gäbe, der dafür verwendet werden könnte. Eine, die nur -1, 0 und 1 im Stil von Ada95 enthält.
Sachin Kainth
23
Die Dokumentation, auf die string::compare()Sie verlinken, gibt eindeutig an, dass der Rückgabewert <0, 0 und> 0 -not- -1, 0 und 1 ist.
Captain Obvlious
6
Was wäre der Vorteil von shortoder charanstelle von int? Die meisten Architekturen speichern den Rückgabewert einer Funktion in einem Register, und ein Wert intpasst genauso gut in ein Register wie ein shortoder char. Die Verwendung charfür numerische Typen ist immer eine schlechte Idee, insbesondere wenn Sie sicherstellen müssen, dass signierte Werte korrekt behandelt werden.
Cody Gray
7
Captain Obvlious, Ihr Name und Kommentar ... Einfach unbezahlbar.
Cody Smith
2
Die Verwendung charwäre eine schlechte Idee, da die Codeprüfung für den Rückgabewert, wenn er kleiner als Null ist, auf Plattformen charohne Vorzeichen fehlschlägt .
Milleniumbug

Antworten:

113

Erstens lautet die Spezifikation, dass ein Wert zurückgegeben wird, der kleiner, gleich oder größer als 0, nicht unbedingt -1oder ist 1. Zweitens sind Rückgabewerte r-Werte, die einer integralen Heraufstufung unterliegen. Es macht also keinen Sinn, etwas Kleineres zurückzugeben.

In C ++ (wie in C) ist jeder Ausdruck entweder ein r-Wert oder ein l-Wert. Historisch gesehen beziehen sich die Begriffe auf die Tatsache, dass l-Werte links von einer Zuweisung angezeigt werden, während r-Werte nur rechts angezeigt werden können. Heutzutage ist eine einfache Annäherung für Nicht-Klassentypen, dass ein l-Wert eine Adresse im Speicher hat, ein r-Wert nicht. Daher können Sie die Adresse eines r-Werts nicht annehmen, und cv-Qualifizierer (die Bedingung "Zugriff") gelten nicht. In C ++ ist ein r-Wert ohne Klassentyp ein reiner Wert, kein Objekt. Der Rückgabewert einer Funktion ist ein r-Wert, sofern er keinen Referenztyp hat. (Nicht-Klassentypen, die in ein Register passen, werden beispielsweise fast immer in einem Register und nicht im Speicher zurückgegeben.)

Für Klassentypen sind die Ausgaben ein wenig komplexer, aufgrund der Tatsache , dass Sie können Mitgliederfunktionen auf rvalue nennen. Dies bedeutet, dass r-Werte tatsächlich Adressen für den this Zeiger haben müssen und cv-qualifiziert sein können, da die cv-Qualifikation eine Rolle bei der Überlastungsauflösung spielt. Schließlich führt C ++ 11 einige neue Unterscheidungen ein, um rvalue-Referenzen zu unterstützen. Auch diese gelten hauptsächlich für Klassentypen.

Integrale Förderung bezieht sich auf die Tatsache, dass Integraltypen, die kleiner als a intsind, in den meisten Kontexten als Werte in einem Ausdruck verwendet werden, zu denen sie befördert werden int. Also selbst wenn ich eine Variable deklariert short a, b;, im Ausdruck a + b, die beide aund bwerden gefördert , intbevor die Zugabe auftritt. In ähnlicher Weise wird, wenn ich schreibe a < 0, der Vergleich mit dem Wert von durchgeführt a, der in einen konvertiert wird int. In der Praxis gibt es nur sehr wenige Fälle, in denen dies einen Unterschied macht, zumindest bei 2-Komplement-Maschinen, bei denen ganzzahlige arithmetische Wraps vorhanden sind (dh heute alle bis auf wenige Exoten - ich denke, die Unisys-Mainframes sind die einzigen verbleibenden Ausnahmen). Trotzdem auch auf den gängigsten Maschinen:

short a = 1;
std::cout << sizeof( a ) << std::endl;
std::cout << sizeof( a + 0 ) << std::endl;

sollte unterschiedliche Ergebnisse liefern: das erste ist das Äquivalent von sizeof( short ), das zweite sizeof( int )(wegen integraler Förderung).

Diese beiden Probleme sind formal orthogonal. rWerte und lWerte haben nichts mit integraler Förderung zu tun. Außer ... integrale Werbung gilt nur für r-Werte, und die meisten (aber nicht alle) Fälle, in denen Sie einen r-Wert verwenden würden, führen zu integraler Werbung. Aus diesem Grund gibt es wirklich keinen Grund, einen numerischen Wert in etwas kleinerem als zurückzugeben int. Es gibt sogar einen sehr guten Grund, es nicht als Zeichentyp zurückzugeben. Überladene Operatoren <<verhalten sich beispielsweise für Zeichentypen häufig anders, sodass Sie nur Zeichen als Zeichentypen zurückgeben möchten. (Sie könnten den Unterschied vergleichen:

char f() { return 'a'; }
std::cout << f() << std::endl;      //  displays "a"
std::cout << f() + 0 << std::endl;  //  displays "97" on my machine

Der Unterschied besteht darin, dass im zweiten Fall die Hinzufügung dazu geführt hat, dass eine integrale Förderung erfolgt, was zu einer anderen <<zu wählenden Überlastung führt .

James Kanze
quelle
46
Es wäre schön, wenn Sie return values are rvalues, subject to integral promotionin Ihrer Antwort mehr erklären könnten .
Alvin Wong
"Rückgabewerte sind r-Werte ... es macht also keinen Sinn, etwas Kleineres zurückzugeben" LIKE IT
masoud
1
@AlvinWong: Siehe die Antworten auf Warum sind C-Zeichen-Literale Ints anstelle von Zeichen? Weitere Hintergrundinformationen.
Jesse Good
Ich wünschte, ich könnte dies wieder +1, nach der hervorragenden Erklärung, die Ihre Bearbeitung hinzugefügt hat.
Cody Gray
Was wäre, wenn es so wäre signed char? Würde es sich wie ein signiertes Verhalten verhalten charoder wäre es ein anderer Typ?
user541686
41

Es ist beabsichtigt, dass -1, 0 oder 1 nicht zurückgegeben werden.

Es erlaubt (beachten Sie, dass dies nicht für Zeichenfolgen gilt, sondern auch für Zeichenfolgen)

int compare(int *a, int *b)
{
   return *a - *b;
}

Das ist viel weniger umständlich als:

int compare(int *a, int *b)
{
   if (*a == *b) return 0;
   if (*a > *b) return 1;
   return -1;
}

Das ist, was Sie tun müssten [oder etwas in dieser Richtung], wenn Sie -1, 0 oder 1 zurückgeben müssten.

Und es funktioniert auch für komplexere Typen:

class Date
{
    int year;
    int month;
    int day;
}

int compare(const Date &a, const Date &b)
{
   if (a.year != b.year) return a.year - b.year;
   if (a.month != b.month) return a.month - b.month;
   return a.day - b.day;
}

Im String-Fall können wir dies tun:

int compare(const std::string& a, const std::string& b)
{
   int len = min(a.length(), b.length());

   for(int i = 0; i < len; i++)
   {
      if (a[i] != b[i]) return a[i] - b[i];
   }
   // We only get here if the string is equal all the way to one of them
   // ends. If the length isn't equal, "longest" wins. 
   return a.length() - b.length();
}
Mats Petersson
quelle
8
Ihre erste compareFunktion hat Probleme mit dem Überlauf, die (zum Glück) nicht gleichermaßen zutreffen, wenn sie dauert char*und charkleiner als ist int. Wenn beispielsweise *aist MAX_INTund *bist, -1dann *a - *bist UB, aber wenn die Implementierung ihr Verhalten definiert, ist das Ergebnis mit ziemlicher Sicherheit negativ.
Steve Jessop
1
Problem mit Ihrem letzten Beispiel: length()size_tint
Gibt
Ja, das kann ein Problem sein, wenn Ihre Zeichenfolgen länger als 2 GB sind. Ich habe 1 GB lange Zeichenfolgen als Testfall für das einmalige Speichern von Dingen in einem Fifo erstellt. Aber sicher, jemand, der sich mit einer Zeichenfolge befasst, die ein als Base64 codiertes MPEG oder ein ähnliches enthält, kann auf dieses Problem
Mats Petersson
@MatsPetersson ist eher ein grundlegendes Problem, da die Frage lautet: "Warum gibt es ein int zurück?"
F'x
Nun, ich bin sicher, dass das hysterisch ist - ich meine historische Gründe - und wahrscheinlich, damit es mit strcmp / memcmp und anderen Vergleichstypoperationen kompatibel ist.
Mats Petersson
25

int ist normalerweise (dh auf der meisten modernen Hardware) eine Ganzzahl mit der gleichen Größe wie der Systembus und / oder die CPU-Register, was als Maschinenwort bezeichnet wird. Daher wird int normalerweise schneller weitergegeben als kleinere Typen, da keine Ausrichtung, Maskierung und andere Operationen erforderlich sind.

Die kleineren Typen existieren hauptsächlich, um die Optimierung der RAM-Nutzung für Arrays und Strukturen zu ermöglichen. In den meisten Fällen tauschen sie einige CPU-Zyklen (in Form von Aligment-Operationen) gegen eine bessere RAM-Nutzung.

Wenn Sie Ihren Rückgabewert nicht als signierte oder nicht signierte Centain-Nummer (char, short…) erzwingen müssen, ist es besser, int zu verwenden, weshalb die Standardbibliothek dies tut.

Tobia
quelle
Eine großartige Möglichkeit, die Hardware-Seite der Dinge auf sinnvolle Weise zu erklären.
Oger Psalm33
10

Es ist ein C-Ismus.

Wenn C compareFunktionen vom Typ Typ benötigte , gaben sie immer eine zurück int. C ++ hat das (leider) gerade übernommen.

Die Rückgabe von intist jedoch realistisch gesehen wahrscheinlich der schnellste Weg, da es sich im Allgemeinen um die Größe der Register des verwendeten Systems handelt. (Absichtlich vage.)

Alex Chamberlain
quelle
1
Tatsächlich shortund charkann Leistungseinbußen nach sich ziehen, z. B. 255+7hat ein anderer Wert für a charund intso kann eine korrekte Implementierung nicht unbedingt einfach speichern, charwohin ein intgehen kann, ohne sich um die Übergabe seiner Semantik zu kümmern. Compiler optimieren nicht unbedingt die damit verbundene Ineffizienz.
Jack Aidley
10

Die Methode gibt keine Ganzzahl in der Menge zurück { -1, 0, 1 }. es kann tatsächlich ein beliebiger ganzzahliger Wert sein.

Warum? Der Hauptgrund, an den ich denken kann, ist, dass intder Wert der "natürlichen Größe" für die Architektur sein soll; Operationen mit Werten dieser Größe sind normalerweise mindestens genauso schnell (und in vielen Fällen schneller) wie Operationen mit kleineren oder größeren Werten. Dies ist also ein Fall, in dem die Implementierung genügend Spielraum hat, um das zu verwenden, was am schnellsten ist.

Jon
quelle
4

Wenn ich eine Vergleichsmethode entwerfen würde, die zwei Objekte vom Typ Foo vergleicht und nur -1, 0 oder 1 zurückgeben möchte, wäre die Verwendung von short oder char im Allgemeinen eine gute Idee?

Es wäre ok Idee. Ein besserer Weg wäre, einen Bool (wenn Sie nur vergleichen möchten, wenn er gleich ist) oder eine Aufzählung (für weitere Informationen) zurückzugeben:

enum class MyResult
{
  EQUAL,
  LESS,
  GREATER
};

MyResult AreEqual( const Foo &foo1, const Foo & foo2 )
{
  // calculate and return result
}
BЈовић
quelle
3
"Es wäre ok Idee". Haben Sie eine Begründung dafür?
Jrok
4

Angenommen, einige Leute ändern einen Code von C nach C ++. Sie beschlossen , zu ersetzen , strcmpzu string::compare.

Da strcmpkehrt int, ist es einfacher, string::comparezurückzukehren int, als Geschenk.

masoud
quelle
2

Wahrscheinlich, damit es besser funktioniert, strcmpdas auch diesen Satz von Rückgabewerten hat . Wenn Sie Code portieren möchten, ist es wahrscheinlich intuitiver, Ersetzungen zu haben, die so nah wie möglich sind.

Auch ist der Rückgabewert nicht nur -1, 0oder 1aber <0, 0oder >0.

Wie bereits erwähnt, ist es nicht sinnvoll, die Rendite zu verkleinern, da sie einer integralen Werbung unterliegt .

Shafik Yaghmour
quelle
-1

weil ein boolescher Rückgabewert nur zwei mögliche Werte sein kann (wahr, falsch) und eine Vergleichsfunktion drei mögliche Werte zurückgeben kann (kleiner als, gleich, größer als).

Aktualisieren

Es ist zwar durchaus möglich, einen vorzeichenbehafteten Kurzschluss zurückzugeben, aber wenn Sie wirklich Ihre eigene Vergleichsfunktion implementieren möchten, können Sie einen Nibble- oder Strukturwert mit zwei Booleschen Werten zurückgeben.

MDMoore313
quelle
7
Nirgends in der Frage steht etwas über die Rückgabe eines Booleschen Typs. In der Tat schlägt er speziell shortund charals Alternativen zu int.
Cody Gray