Шаг 379.
Библиотека STL.
Строки. Значение npos

    На этом шаге мы рассмотрим особенности использования значения npos.

    Если поиск не дает результатов, поисковые функции возвращают string::npos. Рассмотрим следующий пример:

std::string s;
std::string::size_type idx;    // Будьте внимательны: другие типы
                               // не подходят!
.   .   .
idx = s.find("substring");
if (idx == std::string::npos) {
  .   .   .
}

    Условие команды if равно true в том и только в том случае, если строка s не содержит подстроку "substring".

    Будьте очень внимательны при использовании строкового значения npos и его типа. При проверке возвращаемых значений всегда имейте в виду тип возвращаемого значения string::size_type, но не int или unsigned; в противном случае сравнение возвращаемого значения с string::npos может не сработать.

    Такое поведение объясняется тем, что при проектировании библиотеки значение npos было определено как число -1:

namespace std {
  tempiate<class charT,
                class traits = char_traits<charT>, 
                class Allocator = allocator<charT> > 
                class basic_string { 
                       public: 
                          typedef typename Allocator::size_type size_type;
                          .   .   .   .
                          static const size_type npos = -1;
                          .   .   .   .
                }
} 

    К сожалению, тип size_type (определяемый распределителем памяти строки) должен быть беззнаковым целым. Распределитель по умолчанию allocator использует в качестве size_type тип size_t. Поскольку -1 преобразуется к беззнаковому целому типу, npos является максимальным беззнаковым значением этого типа. Тем не менее точное значение зависит от конкретного определения типа size_type. К сожалению, на практике максимальные значения оказываются различными. Более того, (unsigned long)-l отличается от (unsigned short)-l (если различаются размеры этих типов). Следовательно, показанное ниже условие может оказаться равным false, если переменная idx равна -1, a idx и string::npos относятся к разным типам:

  idx == std::string::npos

    Пример:

std::string s;
.   .   .   .
int idx = s.find("not found");      // Предположим, возвращается npos
if (idx == std::string::npos) {     // ОШИБКА: сравнение может не работать
  .   .   .   .
}

    Один из способов предотвращения этой ошибки основан на прямой проверке результата поиска:

if (s.find("hi") == std::string::npos) {
  .   .   .   .
}

    Однако довольно часто в программе нужен индекс позиции, в которой было найдено совпадение. Другое простое решение заключается в определении собственного знакового значения для npos:

const int NPOS = -1;

    Сравнение принимает несколько иной (и даже более компактный) вид:

if (idx == NPOS) {     // Работает почти всегда
  .   .   .   .
}

    К сожалению, это решение не идеально - проверка завершится неудачей, если idx относится к типу unsigned short или индекс превышает максимальное значение int (именно из-за этих проблем такое решение не было стандартизировано). Однако поскольку на практике обе ситуации встречаются очень редко, обычно это решение работает. Но если вы хотите, чтобы программа была действительно переносимой, всегда используйте тип string::size_tyре для всех индексов строковых типов. В идеальном решении вам придется определить перегруженные функции в зависимости от конкретного типа string::size_type. Вероятно, в будущем в стандарте появится более удачное решение этой проблемы.

    Со следующего шага мы начнем рассматривать поддержку итераторов для строк.




Предыдущий шаг Содержание Следующий шаг