Доступ к элементам в std :: string, где положение строки больше ее размера
В случае std :: string, если мы обращаемся к элементу, где (element position) == (size of string)
стандарт говорит, что он возвращает ссылку на объект типа charT
со значением charT()
.
const_reference operator[](size_type pos) const;
reference operator[](size_type pos);
Ожидается: pos <= size().
Возвращает: * (begin() + pos), если pos <size(). В противном случае возвращает ссылку на объект типа charT со значением charT(), где изменение объекта на любое значение, отличное от charT(), приводит к неопределенному поведению.
http://eel.is/c++draft/strings#string.access-1
К сожалению, я не мог рассуждать об этом, было бы лучше, если бы это было неопределенное поведение.
Может кто-нибудь объяснить обоснование этого?
Ответы
Ответ 1
Вы должны рассмотреть полные спецификации.
Прежде всего:
Ожидается: pos <= size().
Если вы не следуете предварительному условию, вы все равно будете иметь неопределенное поведение. Сейчас...
Возвращает: * (begin() + pos), если pos <size(). В противном случае возвращает ссылку на объект типа charT со значением charT(), где изменение объекта на любое значение, отличное от charT(), приводит к неопределенному поведению.
Единственный (действительный) случай, на который ссылается "иначе", это когда pos == size()
. И это, вероятно, для эмуляции поведения c-строки, имеющего элемент some_string[size]
которому можно получить доступ. Обратите внимание, что charT()
обычно просто '\0'
.
PS: Можно подумать, что для реализации спецификации, operator[]
должен проверить, если pos == size
. Однако, если базовый массив символов имеет charT()
в конце строки, то описанное поведение вы получаете в основном бесплатно. Следовательно, то, что кажется немного отличным от "обычного" доступа к массиву, на самом деле является именно этим.
Ответ 2
Утверждение 1 является предварительным условием для утверждения 2:
-
Ожидается: pos <= size()
.
-
Возвращает: *(begin() + pos) if pos < size()
.
В противном случае (поэтому здесь единственно возможная возможность - pos == size()
), возвращает ссылку на объект типа charT
со значением charT()
(то есть '\0'
), где изменение объекта на любое значение, отличное от charT()
приводит к неопределенному поведению.
str[str.size()]
основном указывает на символ нулевого терминатора. Вы можете прочитать и написать это, но вы можете только написать '\0'
в него.
Ответ 3
Оператор ожидает, что pos
будет меньше или равно size()
, поэтому, если оно не меньше, то ожидается, что оно будет равно.
Ответ 4
В дополнение к предыдущим ответам, пожалуйста, посмотрите, что libcxx
(реализация llvm) определяет std::string::operator[]
как:
template <class _CharT, class _Traits, class _Allocator>
inline
typename basic_string<_CharT, _Traits, _Allocator>::const_reference
basic_string<_CharT, _Traits, _Allocator>::operator[](size_type __pos) const _NOEXCEPT
{
_LIBCPP_ASSERT(__pos <= size(), "string index out of bounds");
return *(data() + __pos);
}
template <class _CharT, class _Traits, class _Allocator>
inline
typename basic_string<_CharT, _Traits, _Allocator>::reference
basic_string<_CharT, _Traits, _Allocator>::operator[](size_type __pos) _NOEXCEPT
{
_LIBCPP_ASSERT(__pos <= size(), "string index out of bounds");
return *(__get_pointer() + __pos);
}
Посмотрите на .at()
который правильно выбрасывает вместо этого.
template <class _CharT, class _Traits, class _Allocator>
typename basic_string<_CharT, _Traits, _Allocator>::const_reference
basic_string<_CharT, _Traits, _Allocator>::at(size_type __n) const
{
if (__n >= size())
this->__throw_out_of_range();
return (*this)[__n];
}
Как вы можете заметить, в первом случае есть assert во время выполнения (спасибо t.niese за указание), который запускается только в режиме отладки, тогда как второй всегда выбрасывает, независимо от параметров сборки библиотеки.