Perché `std :: string :: find ()` non restituisce l'iteratore finale in caso di errori?


29

Trovo che il comportamento std::string::findsia incompatibile con i contenitori C ++ standard.

Per esempio

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

Ma per una stringa,

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

Perché il reso non dovrebbe invece essere myStr.find('!')restituito ?myStr.end()std::string::npos

Dal momento che std::stringè un po 'speciale rispetto ad altri contenitori, mi chiedo se ci sia qualche vero motivo dietro questo. (Sorprendentemente, non sono riuscito a trovare nessuno in discussione da nessuna parte).


5
Penso che solo una risposta ragionevole sia vicina alla domanda: "Perché gli hot dog sono confezionati in 4 e gli hot dog in 6?" Bene, è il modo in cui il mondo sarà
felice

Controlla questo
NutCracker,

IMHO, una ragione di questo comportamento sarebbe che std::stringinternamente consiste di personaggi che sono elementi economici (rispetto alla memoria). E, inoltre, il carattere è l'unico tipo che std::stringpuò contenere. D'altra parte, è std::mapcostituito da elementi più complessi. Inoltre, la specifica di std::map::finddice che dovrebbe trovare un elemento, e la specifica di std::string::finddice che il suo compito è trovare la posizione.
Schiaccianoci,

Per la mappa, non è possibile avere un iteratore npos, quindi viene utilizzato l'iteratore finale. Per la stringa, possiamo usare npos, quindi perché no :)
LF

Risposte:


28

Per cominciare, l' std::stringinterfaccia è ben nota per essere gonfia e incoerente, vedere Gotw84 di Herb Sutter su questo argomento. Ma comunque, c'è un ragionamento che sta dietro std::string::findil ritorno di un indice: std::string::substr. Questa funzione di membro di convenienza opera su indici, ad es

const std::string src = "abcdefghijk";

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

Potresti implementare in modo substrtale che accetti iteratori nella stringa, ma non dovremmo aspettare a lungo per lamentele forti che std::stringsono inutilizzabili e controintuitive. Quindi, dato che std::string::substraccetta gli indici, come troveresti l'indice della prima occorrenza 'd'nella stringa di input sopra per stampare tutto a partire da questa sottostringa?

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

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

Questo potrebbe anche non essere quello che vuoi. Quindi possiamo lasciare std::string::findrestituire un indice, ed eccoci qui:

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

Se vuoi lavorare con iteratori, usa <algorithm>. Ti permettono di quanto sopra come

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

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

4
Buon punto. Tuttavia, invece di restituire un iteratore, std::string::findpotrebbe comunque restituire size(), invece di npos, mantenere la compatibilità con substr, evitando anche diversi vantaggi aggiuntivi.
Erenon,

1
@erenon Forse, ma std::string::substrcopre già il caso "inizia qui fino alla fine" con un parametro predefinito per il secondo indice ( npos). Immagino che tornare size()sarebbe anche fonte di confusione e di avere una sentinella letterale comenpos potrebbe essere la scelta migliore ?!
lubgr,

@lubgr Ma se std::string::findrestituisce un iteratore, std::string::substrprobabilmente accetterebbe anche un iteratore per la posizione iniziale. Il tuo esempio con find sembrerebbe lo stesso in entrambi i casi in questo mondo alternativo.
Mattias Wallin il

@MattiasWallin Buon punto. Ma std::string::substrcon un argomento iteratore si aprono le porte per un ulteriore caso UB (oltre allo scenario del passato che può accadere altrettanto bene con indici o iteratori): passare un iteratore che fa riferimento a un'altra stringa.
lubgr,

3

Questo perché std::stringhanno due interfacce:

  • L' interfaccia basata su iteratore generale si trova su tutti i contenitori
  • L' interfaccia std::stringspecifica basata su indice

std::string::findfa parte dell'interfaccia basata su indice e quindi restituisce indici.

Utilizzare std::findper utilizzare l'interfaccia basata su iteratore generale.

Utilizzare std::vector<char>se non si desidera l'interfaccia basata su indice (non farlo).

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.