Warum gibt `std :: string :: find ()` bei Fehlern den Enditerator nicht zurück?


29

Ich finde das Verhalten von std::string::findinkonsistent mit Standard-C ++ - Containern.

Z.B

std::map<int, int> myMap = {{1, 2}};
auto it = myMap.find(10);  // it == myMap.end()

Aber für eine Schnur,

std::string myStr = "hello";
auto it = myStr.find('!');  // it == std::string::npos

Warum sollte die fehlgeschlagene myStr.find('!')Rückkehr nicht myStr.end()statt std::string::npos?

Da das std::stringim Vergleich zu anderen Containern etwas Besonderes ist, frage ich mich, ob es einen wirklichen Grund dafür gibt. (Überraschenderweise konnte ich nirgendwo jemanden finden, der dies in Frage stellte).


5
Ich denke, nur eine vernünftige Antwort ist nahe an der Beantwortung der Frage: "Warum sind Hotdogs in 4 und Hotdog-Brötchen in 6 verpackt?" Nun, so war die Welt zufällig
Bartop

Überprüfen Sie dies
NutCracker

IMHO, ein Grund für dieses Verhalten wäre, dass std::stringintern aus Zeichen besteht, die kostengünstige Elemente sind (in Bezug auf das Gedächtnis). Außerdem ist Zeichen der einzige Typ, der std::stringenthalten kann. std::mapBesteht dagegen aus komplexeren Elementen. Außerdem std::map::findsagt die Spezifikation von , dass es ein Element finden soll, und die Spezifikation von std::string::findsagt, dass es seine Aufgabe ist, Position zu finden.
NutCracker

Für die Karte können Sie keinen npos-Iterator haben, daher wird der Enditerator verwendet. Für String können wir npos verwenden, warum also nicht :)
LF

Antworten:


28

Zunächst std::stringist bekannt, dass die Benutzeroberfläche aufgebläht und inkonsistent ist, siehe Herb Sutters Gotw84 zu diesem Thema. Es gibt jedoch eine Begründung für die std::string::findRückgabe eines Index : std::string::substr. Diese Convenience-Member-Funktion arbeitet mit Indizes, z

const std::string src = "abcdefghijk";

std::cout << src.substr(2, 5) << "\n";

Sie könnten so implementieren substr, dass Iteratoren in die Zeichenfolge aufgenommen werden, aber dann müssten wir nicht lange auf laute Beschwerden warten, std::stringdie unbrauchbar und nicht intuitiv sind. Wenn Sie also std::string::substrIndizes akzeptieren, wie würden Sie den Index des ersten Auftretens 'd'in der obigen Eingabezeichenfolge finden, um alles ausgehend von dieser Teilzeichenfolge auszudrucken?

const auto it = src.find('d'); // imagine this returns an iterator

std::cout << src.substr(std::distance(src.cbegin(), it));

Dies ist möglicherweise auch nicht das, was Sie wollen. Daher können wir std::string::findeinen Index zurückgeben lassen, und hier sind wir:

const std::string extracted = src.substr(src.find('d'));

Wenn Sie mit Iteratoren arbeiten möchten, verwenden Sie <algorithm>. Sie erlauben Ihnen die oben genannten als

auto it = std::find(src.cbegin(), src.cend(), 'd');

std::copy(it, src.cend(), std::ostream_iterator<char>(std::cout));

4
Guter Punkt. Anstatt einen Iterator zurückzugeben, std::string::findkönnte jedoch immer noch zurückgegeben werden size(), anstatt nposdie Kompatibilität mit substrbeizubehalten und gleichzeitig einige zusätzliche Vorteile zu vermeiden.
Erenon

1
@erenon Vielleicht, std::string::substrdeckt aber bereits den Fall "Start hier bis zum Ende" mit einem Standardparameter für den zweiten Index ( npos) ab. Ich denke, eine Rückkehr size()wäre auch verwirrend und ein buchstäblicher Wächter wie nposkönnte die bessere Wahl sein?!
lubgr

@lubgr Wenn aber std::string::findein Iterator zurückgegeben wird, std::string::substrwürde wahrscheinlich auch ein Iterator für die Startposition akzeptiert. Ihr Beispiel mit find würde in dieser alternativen Welt in beiden Fällen gleich aussehen.
Mattias Wallin

@ MattiasWallin Guter Punkt. Aber std::string::substrmit einem Iterator Argumente öffnet die Tür für einen weiteren UB Fall (neben dem past-the-End - Szenario , das genauso gut mit Indizes oder Iteratoren passieren kann): Leiten einen Iterator, der auf einem andere Zeichenfolge bezieht.
lubgr

3

Dies liegt daran, std::stringdass zwei Schnittstellen vorhanden sind:

  • Die allgemeine iteratorbasierte Schnittstelle aller Container
  • Die std::stringspezifische indexbasierte Schnittstelle

std::string::findist Teil der indexbasierten Schnittstelle und gibt daher Indizes zurück.

Verwenden Sie std::finddiese Option , um die allgemeine iteratorbasierte Schnittstelle zu verwenden.

Verwenden std::vector<char>Sie diese Option, wenn Sie die indexbasierte Schnittstelle nicht möchten (tun Sie dies nicht).

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.