В C ++ 11 и далее std :: string :: operator [] выполняет проверку границ?

Я много раз видел, что std::string::operator[] не выполняет проверку границ. Четное В чем разница между string :: at и string :: operator []?, спросил в 2013 году, ответы говорят, что operator[] не выполняет проверку границ.

Моя проблема с этим, если я смотрю на стандарт (в этом случае проект N3797) в [string.access] мы имеем

const_reference operator[](size_type pos) const;
reference operator[](size_type pos);
  1. Требуется: pos <= size(),
  2. Возвращает: *(begin() + pos) если pos < size(), В противном случае возвращает ссылку на объект типа charT со значением charT()где изменение объекта приводит к неопределенному поведению.
  3. Броски: Ничего такого.
  4. Сложность: постоянное время

Это заставляет меня верить, что operator[] должен сделать какую-то проверку границ, чтобы определить, нужно ли возвращать элемент строки или значение по умолчанию charT, Это предположение правильно и operator[] теперь требуется сделать проверку границ?

26

Решение

Формулировка немного сбивает с толку, но если вы изучите ее подробно, вы обнаружите, что она на самом деле очень точная.

Это говорит это:

  • Предварительным условием является то, что аргумент [] либо = N или это < N.
  • Предполагая, что предварительное условие выполнено:
    • Если это < N тогда вы получите персонаж, которого вы просили.
    • «В противном случае» (т.е. если это N) тогда вы получите charT() (то есть нулевой символ).

Но правила не определены, когда вы нарушаете предварительное условие, и чек на = N может быть удовлетворено неявно (но не обязательно), фактически сохраняя charT() в положении N.

Таким образом, реализациям не нужно выполнять какие-либо проверки границ … а обычные не будут.

44

Другие решения

operator[] сделал какую-то проверку границ, чтобы определить …

Нет, это не так. С предварительным условием

Требуется: поз <= размер ().

это может просто ПРЕДПОЛАГАТЬ что он всегда может вернуть элемент строки. Если это условие не выполняется: неопределенное поведение.

operator[] скорее всего, просто увеличит указатель с начала строки на pos. Если строка короче, тогда она просто возвращает ссылку на данные за строкой, какой бы она ни была. Как классика вне границ в простых C-массивах.

Для уточнения случая где pos == size() это могло бы просто выделить дополнительную charT в конце его внутренней строки данных. Таким образом, простое увеличение указателя без каких-либо проверок все равно приведет к заявленному поведению.

14

Во-первых, есть пункт «требуется». Если вы нарушаете условие require, ваша программа ведет себя неопределенным образом. То есть pos <= size(),

Таким образом, язык определяет только то, что происходит в этом случае.

В следующем абзаце говорится, что для pos < size(), он возвращает ссылку на элемент в строке. И для pos == size(), он возвращает ссылку на построенный по умолчанию charT со значением charT(),

Хотя это может выглядеть как проверка границ, на практике происходит то, что std::basic_string выделяет буфер на единицу больше запрашиваемого и заполняет последнюю запись charT(), затем [] просто делает указатель арифметическим.

Я попытался придумать способ избежать этой реализации. Хотя стандарт не предписывает его, я не мог убедить себя, что альтернатива существует. Было что-то раздражающее с .data() это мешало избежать единственного буфера.

4

Этот оператор стандартных контейнеров эмулирует поведение оператора [] обычных массивов. Так что он не делает никаких проверок. Однако в режиме отладки соответствующая библиотека может обеспечить эту проверку.

Если вы хотите проверить индекс, используйте функцию-член at() вместо.

2

http://en.cppreference.com/w/cpp/string/basic_string/operator_at

Возвращает ссылку на символ в указанном месте поз. нет
проверка границ выполняется.

(Акцент мой).

Если вы хотите проверить границы, используйте станд :: basic_string :: на

Стандарт подразумевает, что реализация должна обеспечивать проверку границ, потому что он в основном описывает, что делает доступ без контроля массива.

Если вы получаете доступ в пределах, это определено. Если вы выходите наружу, вы запускаете неопределенное поведение.

1