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).

Sumudu
quelle
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));
lubgr
quelle
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).

Mattias Wallin
quelle