¿Por qué `std :: string :: find ()` no devuelve el iterador final en caso de fallas?


29

Encuentro el comportamiento de std::string::findser inconsistente con los contenedores estándar de C ++.

P.ej

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

Pero por una cuerda,

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

¿Por qué no debería la myStr.find('!')devolución fallida en myStr.end()lugar de std::string::npos?

Dado que std::stringes algo especial en comparación con otros contenedores, me pregunto si hay alguna razón real detrás de esto. (Sorprendentemente, no pude encontrar a nadie cuestionando esto en ninguna parte).


55
Creo que solo una respuesta razonable está cerca de responder a la pregunta: '¿Por qué los perritos calientes se empaquetan en 4 y los hot dogs en 6?' Bueno, así es como será el mundo
— bartop el


En mi humilde opinión, una razón para este comportamiento sería que std::stringinternamente se compone de personajes que son elementos de bajo costo (en lo que respecta a la memoria). Y, además, el carácter es el único tipo que std::stringpuede contener. Por otro lado, std::mapconsta de elementos más complejos. Además, la especificación de std::map::finddice que se supone que debe encontrar un elemento, y la especificación de std::string::finddice que su tarea es encontrar la posición.
— NutCracker

Para el mapa, no puede tener un iterador npos, por lo que se utiliza el iterador final. Para la cadena, podemos usar npos, entonces ¿por qué no :)
— LF

Respuestas:


28

Para empezar, std::stringse sabe que la interfaz es hinchada e inconsistente, vea Gotw84 de Herb Sutter sobre este tema. Pero, sin embargo, hay un razonamiento detrás de std::string::finddevolver un índice: std::string::substr. Esta función de miembro de conveniencia opera en índices, p. Ej.

const std::string src = "abcdefghijk";

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

Puede implementar de substrmodo que acepte iteradores en la cadena, pero no tendríamos que esperar mucho tiempo para recibir quejas fuertes que no std::stringse pueden usar y que no son intuitivas. Entonces, dado que std::string::substracepta índices, ¿cómo encontraría el índice de la primera aparición de 'd'en la cadena de entrada anterior para imprimir todo a partir de esta subcadena?

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

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

Esto también podría no ser lo que quieres. Por lo tanto, podemos dejar que std::string::finddevuelva un índice, y aquí estamos:

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

Si desea trabajar con iteradores, use <algorithm>. Te permiten lo anterior como

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

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

44
Buen punto. Sin embargo, en lugar de devolver un iterador, std::string::findaún podría devolver size(), en lugar de nposmantener la compatibilidad con substr, evitando al mismo tiempo varias ramificaciones adicionales.
— Erenon

1
@erenon Quizás, pero std::string::substrya cubre el caso "comenzar aquí hasta el final" con un parámetro predeterminado para el segundo índice ( npos). Supongo que regresar size()también sería confuso y tener un centinela literal como npospodría ser la mejor opción.
— lubgr

@lubgr Pero si std::string::finddevuelve un iterador, std::string::substrprobablemente también aceptaría un iterador para la posición de inicio. Su ejemplo con find se vería igual en ambos casos en este mundo alternativo.
— Mattias Wallin el

@MattiasWallin Buen punto. Pero std::string::substrcon un argumento iterador se abre la puerta para otro caso de UB (además del escenario pasado-final que igualmente puede suceder con índices o iteradores): pasar un iterador que se refiere a otra cadena.
— lubgr

3

Esto se debe a que std::stringtiene dos interfaces:

  • La interfaz general basada en iterador que se encuentra en todos los contenedores
  • La interfaz basada en índicesstd::string específicos

std::string::findforma parte de la interfaz basada en índices y, por lo tanto, devuelve índices.

Se usa std::findpara usar la interfaz general basada en iterador.

Úselo std::vector<char>si no desea la interfaz basada en índice (no haga esto).

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.