The short answer is that c++17 does provide the char* string::data()
method. Which is vital for the similarly c++17 data
function, thus to gain mutable access to the underlying C-String I can now do this:
auto foo = "lorem ipsum"s;
for(auto i = data(foo); *i != '\0'; ++i) ++(*i);
For historical purposes it's worth chronicling string
's development which c++17 is building upon: In c++11 access to string
's underlying buffer is made possible possible by a new requirement that it's elements are stored contiguously such that for any given string s
:
&*(s.begin() + n) == &*s.begin() + n
for any n
in [0
, s.size()
), or, equivalently, a pointer to s[0]
can be passed to functions that expect a pointer to the first element of a CharT[]
array.
Mutable access to this newly required underlying C-String was obtainable by various methods, for example: &s.front()
, &s[0]
, or &*s.first()
But back to the original question which would avoid the burden of using one of these options: Why hasn't access to string
's underlying buffer been provided in the form of char* string::data()
?
To answer that it is important to note that T* array<T>::data()
and T* vector<T>::data()
were an addition required by c++11. No additional requirements were incurred by c++11 against other contiguous containers such as deque
. And there certainly wasn't an additional requirement for string
, in fact the requirement that string
was contiguous was new to c++11. Before this const char* string::data()
had existed. Though it explicitly was not guaranteed to be pointing to any underlying buffer, it was the only way to obtain a const char*
from a string
:
The returned array is not required to be null-terminated.
This means that string
was not "shortchanged" in c++11's transition to data
accessors, it simply was not included thus only the const
data
accesor that string
previously possessed persisted. There are naturally occurring examples in C++11's implementation which necessitate writing directly to the underlying buffer of a string
.
&s[0]
, for non-empty strings. – Idolistdata
, you probably are looking forvector<char>
, instead. There're some exceptions, though. – Bettorvector<char>
extensively and regretted it every time, that I subsequently copy the characters back into astring
. What I actually want is astring
with a modifiable buffer. – Radiostd::string
does have a modifiable buffer. The issue mentioned by Alper is just that it has inconsistent naming compared to other standard library containers. – Idolist&string::front()
or&*string::begin()
and so on. – Radiochar* string::data()
so I don't have to make a call on thestring
and then take the address of the return to get the buffer. I want a single call on thestring
to return it's modifiable buffer. – Radioconst_cast
, wherein: "Modifying a const object through a non-const access path and referring to a volatile object through a non-volatile glvalue results in undefined behavior." As such I believe we could say, "No, this is definitely not good practice." – Radioint* foo() { int result[] = { 1, 2, 3 }; return result; }
you may see this undefined behavior work for years until someone adjusts the order of events andfoo
's stack frame is overwritten before its return is used. then suddenly you have undefined behavior that was "caused" by code totally unrelated to the problem. As someone who understands such things you have a responsibility to speak against them, or at a minimum not to propagate them by suggesting them in comments. – Radio