Доступ к элементам в std :: string, где положение строки больше ее размера - PullRequest
36 голосов
/ 09 апреля 2019

В случае std :: string, если мы обращаемся к элементу, где (element position) == (size of string), стандарт говорит, что он возвращает ссылку на объект типа charT со значением charT().

const_reference operator[](size_type pos) const;
reference       operator[](size_type pos);

Ожидается: pos <= size (). </p>

Возвращает: * (begin () + pos), если pos

http://eel.is/c++draft/strings#string.access-1

К сожалению, я не мог рассуждать об этом, было бы лучше, если бы это было неопределенное поведение.

Может кто-нибудь объяснить причину этого?

Ответы [ 4 ]

39 голосов
/ 09 апреля 2019

Вы должны рассмотреть полные спецификации.

Прежде всего:

Ожидается: pos <= size (). </p>

Если вы не выполняете предварительное условие, вы все равно будете иметь неопределенное поведение.Теперь ...

Возвращает: * (begin () + pos), если pos

Единственный (действительный) случай, когда«иначе» относится к случаю, когда pos == size().И это, вероятно, для эмуляции поведения c-строки, имеющего элемент some_string[size], к которому можно получить доступ.Обратите внимание, что charT() обычно это просто '\0'.

PS: Можно подумать, что для реализации спецификации operator[] придется проверить, если pos == size.Однако, если базовый массив символов имеет charT() в конце строки, то описанное поведение вы получаете в основном бесплатно.Следовательно, то, что кажется немного отличным от «обычного» доступа к массиву, на самом деле является именно этим.

22 голосов
/ 09 апреля 2019

Оператор 1 является предварительным условием для оператора 2:

  1. Ожидается: pos <= size().

  2. Возвращает: *(begin() + pos) if pos < size().

    В противном случае (, поэтому здесь единственная возможная возможность - pos == size()), возвращает ссылку на объект типа charT со значением charT() (, т. Е. '\0'), где изменение объекта на любое значение, отличное от charT(), приводит к неопределенному поведению.

str[str.size()] в основном указывает на символ нулевого терминатора. Вы можете прочитать и написать это, но вы можете написать только '\0'.

15 голосов
/ 09 апреля 2019

Оператор ожидает, что pos будет меньше или равно size(), поэтому, если оно не меньше, то ожидается, что оно будет равно.

2 голосов
/ 09 апреля 2019

В дополнение к предыдущим ответам, пожалуйста, взгляните на 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];
}

Как вы можете заметить, в первом случае существует утверждение времени выполнения (спасибо t.niese за указание), которое запускается только в режиме отладки, тогда как второе всегда будет выдавать независимо от сборкиварианты библиотеки.

...