Я много раз видел, что std::string::operator[]
не выполняет проверку границ. Четное В чем разница между string :: at и string :: operator []?, спросил в 2013 году, ответы говорят, что operator[]
не выполняет проверку границ.
Моя проблема с этим, если я смотрю на стандарт (в этом случае проект N3797) в [string.access] мы имеем
const_reference operator[](size_type pos) const; reference operator[](size_type pos);
- Требуется:
pos <= size()
,- Возвращает:
*(begin() + pos)
еслиpos < size()
, В противном случае возвращает ссылку на объект типаcharT
со значениемcharT()
где изменение объекта приводит к неопределенному поведению.- Броски: Ничего такого.
- Сложность: постоянное время
Это заставляет меня верить, что operator[]
должен сделать какую-то проверку границ, чтобы определить, нужно ли возвращать элемент строки или значение по умолчанию charT
, Это предположение правильно и operator[]
теперь требуется сделать проверку границ?
Формулировка немного сбивает с толку, но если вы изучите ее подробно, вы обнаружите, что она на самом деле очень точная.
Это говорит это:
[]
либо = N или это < N.charT()
(то есть нулевой символ).Но правила не определены, когда вы нарушаете предварительное условие, и чек на = N может быть удовлетворено неявно (но не обязательно), фактически сохраняя charT()
в положении N.
Таким образом, реализациям не нужно выполнять какие-либо проверки границ … а обычные не будут.
operator[]
сделал какую-то проверку границ, чтобы определить …
Нет, это не так. С предварительным условием
Требуется: поз <= размер ().
это может просто ПРЕДПОЛАГАТЬ что он всегда может вернуть элемент строки. Если это условие не выполняется: неопределенное поведение.
operator[]
скорее всего, просто увеличит указатель с начала строки на pos. Если строка короче, тогда она просто возвращает ссылку на данные за строкой, какой бы она ни была. Как классика вне границ в простых C-массивах.
Для уточнения случая где pos == size()
это могло бы просто выделить дополнительную charT
в конце его внутренней строки данных. Таким образом, простое увеличение указателя без каких-либо проверок все равно приведет к заявленному поведению.
Во-первых, есть пункт «требуется». Если вы нарушаете условие require, ваша программа ведет себя неопределенным образом. То есть pos <= size()
,
Таким образом, язык определяет только то, что происходит в этом случае.
В следующем абзаце говорится, что для pos < size()
, он возвращает ссылку на элемент в строке. И для pos == size()
, он возвращает ссылку на построенный по умолчанию charT
со значением charT()
,
Хотя это может выглядеть как проверка границ, на практике происходит то, что std::basic_string
выделяет буфер на единицу больше запрашиваемого и заполняет последнюю запись charT()
, затем []
просто делает указатель арифметическим.
Я попытался придумать способ избежать этой реализации. Хотя стандарт не предписывает его, я не мог убедить себя, что альтернатива существует. Было что-то раздражающее с .data()
это мешало избежать единственного буфера.
Этот оператор стандартных контейнеров эмулирует поведение оператора [] обычных массивов. Так что он не делает никаких проверок. Однако в режиме отладки соответствующая библиотека может обеспечить эту проверку.
Если вы хотите проверить индекс, используйте функцию-член at()
вместо.
http://en.cppreference.com/w/cpp/string/basic_string/operator_at
Возвращает ссылку на символ в указанном месте поз. нет
проверка границ выполняется.
(Акцент мой).
Если вы хотите проверить границы, используйте станд :: basic_string :: на
Стандарт подразумевает, что реализация должна обеспечивать проверку границ, потому что он в основном описывает, что делает доступ без контроля массива.
Если вы получаете доступ в пределах, это определено. Если вы выходите наружу, вы запускаете неопределенное поведение.