By AImx1


2019-04-09 08:18:02 8 Comments

In case of std::string, if we access an element where (element position) == (size of string) the standard says that it returns a reference to an object of type charT with value charT().

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

Expects: pos <= size().

Returns: *(begin() + pos) if pos < size(). Otherwise, returns a reference to an object of type charT with value charT(), where modifying the object to any value other than charT() leads to undefined behavior.

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

Unfortunately I couldn't reason about this, it would have been better if it has been Undefined Behavior.

Can somebody explain the rationale behind this?

4 comments

@rustyx 2019-04-09 08:36:14

Statement 1 is the precondition for statement 2:

  1. Expects: pos <= size().

  2. Returns: *(begin() + pos) if pos < size().

    Otherwise (so here the only viable possibility is pos == size()), returns a reference to an object of type charT with value charT() (i.e. '\0'), where modifying the object to any value other than charT() leads to undefined behavior.

str[str.size()] basically points to the null-terminator character. You can read and write it, but you may only write a '\0' into it.

@Yola 2019-04-09 08:34:40

The operator expects pos to be less than or equal to size(), so if it is not less, then it is expected to be equal.

@KostasRim 2019-04-09 08:47:44

Additionally to the previous answers please take a look at the libcxx (the llvm implementation) defines std::string::operator[] like:

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);
}

Take a look at the .at() that properly throws instead.

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];
}

As you can, in the first case, there is a runtime assert(thanks t.niese for pointing out) which is triggered only in debug mode whereas the second will always throw, regardless of the build options of the library.

@t.niese 2019-04-09 08:52:26

That's not a static assert it is a runtime assert. A static_assert is something that is check at compile time, and a static assert is done for both release and debug builds.

@AImx1 2019-04-09 08:57:10

std::string name = "StackOverflow"; std::cout << name[100]; @t.niese If it a run time assert, why does the below code doesn't crash?

@KostasRim 2019-04-09 09:00:39

@AImx1 cause you didn't specify that you want the debug build when building the library?

@t.niese 2019-04-09 09:02:01

@AImx1 because the standard says that name[100] is undefined behavior, and not that it must throw. _LIBCPP_ASSERT is a debugging assert that has to be explicitly enabled and is not automatically enable for regular debug builds, and it is run-time dependent llvm: DebugMode

@Arthur Tacca 2019-04-09 14:45:34

Also, putting aside the _LIBCPP_ASSERT, what this code snippet really shows is there is no test like if (__pos >= size()) return _CharT(), which is what would be needed for the behaviour the original question was expecting. Without a test like this, the only way _CharT() could be returned is if it is stored in the buffer pointed to by data(). Obviously this can't the case for all possible values of __pos, unless the buffer takes up all the memory on your computer!

@user463035818 2019-04-09 08:35:17

You have to consider the full specs.

First of all:

Expects: pos <= size().

If you dont follow the precondition you have undefined behaviour anyhow. Now...

Returns: *(begin() + pos) if pos < size(). Otherwise, returns a reference to an object of type charT with value charT(), where modifying the object to any value other than charT() leads to undefined behavior.

The only (valid) case that "otherwise" refers to is when pos == size(). And that is probably to emulate c string behaviour that have a some_string[size] element that can be accessed. Note that charT() is typically just '\0'.

PS: One might think that to implement the specification, operator[] would have to check if pos == size. However, if the underlying character array has a charT() at the end of the string, then you get the described behaviour basically for free. Hence, what seems a little different from "usual" access into an array is actually just that.

@xtofl 2019-04-10 05:27:17

Great explanation. Also a great example how explaining unsupported cases (>size) leads to harder to follow specs. 'otherwise' should have read 'if pos==size'

Related Questions

Sponsored Content

24 Answered Questions

[SOLVED] How to convert std::string to lower case?

40 Answered Questions

[SOLVED] What's the best way to trim std::string?

  • 2008-10-19 19:23:07
  • Milan Babu┼íkov
  • 596587 View
  • 707 Score
  • 40 Answer
  • Tags:   c++ trim stdstring

12 Answered Questions

[SOLVED] std::wstring VS std::string

13 Answered Questions

[SOLVED] Are the days of passing const std::string & as a parameter over?

  • 2012-04-19 15:20:57
  • Benj
  • 155571 View
  • 551 Score
  • 13 Answer
  • Tags:   c++ c++11

3 Answered Questions

[SOLVED] What made i = i++ + 1; legal in C++17?

11 Answered Questions

[SOLVED] Why is f(i = -1, i = -1) undefined behavior?

8 Answered Questions

[SOLVED] How to convert a std::string to const char* or char*?

  • 2008-12-07 19:30:56
  • user37875
  • 854924 View
  • 828 Score
  • 8 Answer
  • Tags:   c++ string char const

5 Answered Questions

[SOLVED] In C++11 and beyond does std::string::operator[] do bounds checking?

1 Answered Questions

[SOLVED] Does std::string find require that pos be less than the string size?

Sponsored Content