§21.4.5 [string.access]
const_reference operator[](size_type pos) const;
reference operator[](size_type pos);
Returns:
*(begin() + pos)ifpos < size(). Otherwise, returns a reference to an object of typecharTwith valuecharT(), where modifying the object leads to undefined behavior.
The second part implies, to me atleast, that this "object of type charT" may reside outside of the sequence stored in the std::string object. Example implementation of a conforming operator[]:
reference operator[](size_type pos){
static contexpr charT default = charT();
if(pos == size())
return default;
return buf[pos];
}
Now, c_str()/data(), are specified in terms of operator[]:
§21.4.7 [string.accessors]
const charT* c_str() const noexcept;
const charT* data() const noexcept;
Returns: A pointer
psuch thatp + i == &operator[](i)for eachiin[0,size()].
This would make the above operator[] implementation non-conformant, as p + size() != &operator[](size()). However, with a bit of trickery, you can circumvent this problem:
reference operator[](size_type pos){
static contexpr charT default = charT();
if(pos == size() && !evil_context) // assume 'volatile bool evil_context;'
return default;
return buf[pos];
}
struct evil_context_guard{
volatile bool& ctx;
evil_context_guard(volatile bool& b)
: ctx(b) {}
~evil_context_guard(){ b = false; }
};
const charT* c_str() const noexcept{
evil_context_guard g(evil_context = true);
// now, during the call to 'c_str()', the requirement above holds
// 'p + i == &operator[](i) for each i in [0,size()]'
const charT* p = &buf[0];
assert(p+size() == &operator[](size()));
return p;
}
Now, the obvious question is...
Is the above code really conformant or did I overlook something?