Почему `std :: string :: find ()` не возвращает конечный итератор при сбоях?

29

Я считаю, что поведение std::string::findнесовместимо со стандартными контейнерами C ++.

Например

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

Но для строки,

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

Почему бы не myStr.find('!')вернуть неудачу myStr.end()вместо std::string::npos?

Поскольку std::stringон несколько особенный по сравнению с другими контейнерами, мне интересно, есть ли какая-то реальная причина этого. (Удивительно, но я не смог найти никого, кто бы это допрашивал).

Sumudu
источник
5
Я думаю, что только разумный ответ близок к ответу на вопрос: «Почему хот-доги упакованы в 4, а булочки в 6?» Ну, это как мир СЛУЧИЛОСЬ быть
bartop
Проверьте это
NutCracker
ИМХО, причиной такого поведения будет то, что std::stringвнутренне состоит из символов, которые являются недорогими элементами (в отношении памяти). И, кроме того, символ - единственный тип, который std::stringможет содержать. С другой стороны, std::mapсостоит из более сложных элементов. Кроме того, спецификация std::map::findговорит, что он должен найти элемент, а спецификация std::string::findговорит, что его задача - найти позицию.
NutCracker
Для карты у вас не может быть итератора npos, поэтому используется конечный итератор. Для строки мы можем использовать npos, так почему бы и нет :)
LF

Ответы:

28

Начнем с того, что std::stringинтерфейс, как известно, раздутый и противоречивый, см. Gotw84 Херба Саттера на эту тему. Но , тем не менее, есть обоснование std::string::findвозвращения индекса: std::string::substr. Эта удобная функция-член работает с индексами, например

const std::string src = "abcdefghijk";

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

Вы могли бы реализовать так substr, чтобы он принимал итераторы в строку, но тогда нам не пришлось бы долго ждать громких жалоб, которые std::stringнепригодны и противоречивы. Итак, учитывая, что он std::string::substrпринимает индексы, как бы вы нашли индекс первого вхождения 'd'в указанной выше входной строке, чтобы распечатать все, начиная с этой подстроки?

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

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

Это также может быть не то, что вы хотите. Следовательно, мы можем позволить std::string::findвернуть индекс, и вот мы здесь:

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

Если вы хотите работать с итераторами, используйте <algorithm>. Они позволяют вам как выше

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

std::copy(it, src.cend(), std::ostream_iterator<char>(std::cout));
lubgr
источник
4
Хорошая точка зрения. Однако вместо возврата итератора std::string::findон все равно мог бы вернуть size()вместо nposсохранения совместимости substr, а также избежать нескольких дополнительных браков.
Эренон
1
@erenon Возможно, но std::string::substrуже охватывает случай «от начала до конца» с параметром по умолчанию для второго индекса ( npos). Я думаю, что возвращение size()также может сбить с толку, и иметь буквального дозорного, как nposможет быть лучшим выбором ?!
lubgr
@lubgr Но если std::string::findвозвращает итератор, std::string::substrвероятно, также принял бы итератор для начальной позиции. Ваш пример с find будет выглядеть одинаково в обоих случаях в этом альтернативном мире.
Маттиас Уоллин
@MattiasWallin Хороший вопрос. Но std::string::substrс аргументом итератора открывается дверь для еще одного случая UB (помимо сценария «за конец», который может одинаково хорошо происходить с индексами или итераторами): передача итератора, который ссылается на другую строку.
Lubgr
3

Это потому std::string, что есть два интерфейса:

  • Общий интерфейс на основе итератора, найденный во всех контейнерах
  • std::stringКонкретный индекс на основе интерфейса

std::string::findявляется частью интерфейса, основанного на индексах , и поэтому возвращает индексы.

Используйте std::findдля использования общего интерфейса на основе итератора.

Используйте, std::vector<char>если вам не нужен интерфейс на основе индекса (не делайте этого).

Маттиас Валлин
источник