Dlaczego `std :: string :: find ()` nie zwraca iteratora końcowego w przypadku awarii?

29

Uważam zachowanie std::string::find jest niespójne ze standardowymi kontenerami C ++.

Na przykład

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

Ale dla sznurka

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

Dlaczego zamiast nieudanego myStr.find('!')powrotu ?myStr.end()std::string::npos

Ponieważ std::stringjest to coś specjalnego w porównaniu z innymi pojemnikami, zastanawiam się, czy jest jakiś prawdziwy powód tego. (Zaskakujące, nigdzie nie znalazłem nikogo, kto by to kwestionował).

Sumudu
źródło
5
Myślę, że tylko rozsądna odpowiedź jest bliska odpowiedzi na pytanie: „Dlaczego hot dogi pakowane są w 4, a hotdogi w 6?” Tak właśnie
wygląda
Sprawdź to
NutCracker
IMHO, przyczyną takiego zachowania byłoby to, że std::stringwewnętrznie składają się z znaków, które są niedrogimi elementami (pod względem pamięci). Co więcej, charakter jest jedynym typem, który std::stringmoże zawierać. Z drugiej strony std::mapskłada się z bardziej złożonych elementów. Ponadto specyfikacja std::map::findmówi, że ma znaleźć element, a specyfikacja std::string::findmówi, że jego zadaniem jest znalezienie pozycji.
NutCracker
W przypadku mapy nie można mieć iteratora npos, więc używany jest iterator końcowy. W przypadku ciągu możemy użyć npos, więc dlaczego nie :)
LF

Odpowiedzi:

28

Po pierwsze, std::stringinterfejs jest dobrze znany jako nadęty i niespójny, patrz Gotw84 Herb Suttera na ten temat. Ale mimo wszystko, istnieje uzasadnienie std::string::findzwrotu indeksu: std::string::substr. Ta funkcja członka wygody działa na indeksach, np

const std::string src = "abcdefghijk";

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

Możesz zaimplementować substrtakie, które akceptuje iteratory w ciągu, ale wtedy nie musielibyśmy długo czekać na głośne skargi, które std::stringsą bezużyteczne i sprzeczne z intuicją. Biorąc pod uwagę, że std::string::substrakceptujemy indeksy, w jaki sposób można znaleźć indeks pierwszego wystąpienia 'd'powyższego ciągu wejściowego, aby wydrukować wszystko, zaczynając od tego podłańcucha?

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

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

To może nie być to, czego chcesz. Dlatego możemy pozwolić std::string::findzwrócić indeks, a oto jesteśmy:

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

Jeśli chcesz pracować z iteratorami, użyj <algorithm>. Pozwalają ci na powyższe jako

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

std::copy(it, src.cend(), std::ostream_iterator<char>(std::cout));
lubgr
źródło
4
Słuszna uwaga. Jednak zamiast wracać iterator, std::string::findmoże jeszcze powrócić size(), zamiast npos, zachowując zgodność z substr, jednocześnie unikając kilka dodatkowych brances.
erenon
1
@erenon Być może, ale std::string::substrjuż obejmuje przypadek „zacznij tutaj do końca” domyślnym parametrem dla drugiego indeksu ( npos). Wydaje mi się, że powrót size()byłby mylący, a posiadanie dosłownego wartownika nposmoże być lepszym wyborem ?!
lubgr
@lubgr Ale jeśli std::string::findzwróci iterator, std::string::substrprawdopodobnie również zaakceptuje iterator dla pozycji początkowej. Twój przykład ze znalezieniem wyglądałby tak samo w obu przypadkach w tym alternatywnym świecie.
Mattias Wallin
@MattiasWallin Dobra uwaga. Ale std::string::substrz argumentem iteratora otwiera drzwi do jeszcze jednej sprawy UB (oprócz scenariusza z przeszłości, który równie dobrze może się zdarzyć z indeksami lub iteratorami): przekazanie iteratora, który odnosi się do innego ciągu.
lubgr
3

Jest tak, ponieważ std::stringmają dwa interfejsy:

  • Ogólny iterator interfejs oparty na znaleziony we wszystkich kontenerach
  • std::stringSpecyficzny wskaźnik interfejs oparty

std::string::findjest częścią indeksu interfejsu opartego , dlatego zwraca indeksy.

Posługiwać się std::find aby użyć ogólnego interfejsu opartego na iteratorze.

Użyj, std::vector<char>jeśli nie chcesz interfejsu opartego na indeksie (nie rób tego).

Mattias Wallin
źródło