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.

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?

ANSWER

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.