Чому `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дещо особливе порівняно з іншими контейнерами, мені цікаво, чи є якась реальна причина цього. (Дивно, але я ніде не міг знайти когось, хто ставить під сумнів це питання).


5
Я думаю, що лише розумна відповідь близька до відповіді на питання: "Чому хот-доги упаковані в 4, а булочки в 6"? Ну, це такий, яким має бути світ
bartop

Перевірте це
NutCracker

ІМХО, причиною такої поведінки було б те, що std::stringвнутрішньо складається з персонажів, які є недорогими елементами (що стосується пам'яті). І, крім того, характер є єдиним типом, який std::stringможе містити. З іншого боку, std::mapскладається з більш складних елементів. Крім того, специфікація std::map::findговорить про те, що слід знайти елемент, а специфікація std::string::findговорить про те, що його завдання - знайти позицію.
NutCracker

Для карти ви не можете мати ітератор npos, тому використовується кінцевий ітератор. Для рядка ми можемо використовувати npos, так чому б і ні :)
LF

Відповіді:


28

Для початку std::stringінтерфейс, як відомо, роздутий і непослідовний, див. На цю тему Herb Sutter's 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));

4
Гарна думка. Однак замість повернення ітератора,std::string::find все-таки можна було б повернути size(), замість того npos, щоб зберегти сумісність із substr, одночасно уникаючи декількох зайвих брендів.
еренон

1
@erenon Можливо, але std::string::substrвже охоплює випадок "почати тут до кінця" з параметром за замовчуванням для другого індексу ( npos). Я думаю, повернення size()також буде заплутаним, а nposкращий вибір може бути буквальним дозорним ?!
лубгр

@lubgr Але якщо std::string::findповерне ітератор, std::string::substrймовірно, він також прийме ітератор для початкової позиції. Ваш приклад із знахідкою виглядав би однаково в обох випадках у цьому альтернативному світі.
Маттіас

@MattiasWallin Добрий момент. Алеstd::string::substr аргумент ітератора відкриває двері для ще одного випадку UB (крім сценарію минулого кінця, який може однаково траплятися з індексами чи ітераторами): передача ітератора, що посилається на інший рядок.
лубгр

3

Це тому, що std::stringє два інтерфейси:

  • Загальний інтерфейс на основі ітератора, знайдений у всіх контейнерах
  • std::stringКонкретний індекс на основі інтерфейсу

std::string::findє частиною інтерфейсу на основі індексу , тому повертає індекси.

Використовуйте std::findдля використання загального інтерфейсу на основі ітератора.

Використовуйте std::vector<char> якщо ви не хочете інтерфейсу на основі індексу (не робіть цього).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.