Reputation: 1995
How can I get a pointer to the first element of an non-const std::string even if it is empty.
I know for sure the string has a capacity greater than one, however, simply using
&my_string.front()
triggers a "out of range iterator" assertion failure because the string is empty. Using member function
data()
doesn't fit too, because I want a pointer of type char*, not const char*. I guess I can perform a const_cast here, but that doesn't seem neat.
Is there a better way to do this?
Upvotes: 3
Views: 2023
Reputation: 385204
Although front()
will throw here, my_string[my_string.size()]
is nowadays deemed valid — you can't do much with it, but you can take and store its address (so, &my_string[0]
).
However, there's only questionable value in doing so. So you could probably assume that the first bunch of character insertions (up to the current capacity) won't practically invalidate your pointer, but your code will be of unclear correctness, as we are specifically told by string.require¶4 not to rely on it:
References, pointers, and iterators referring to the elements of a
basic_string
sequence may be invalidated by the following uses of thatbasic_string
object:
- as an argument to any standard library function taking a reference to non-const
basic_string
as an argument.- Calling non-const member functions, except
operator[]
,at
,data
,front
,back
,begin
,rbegin
,end
, andrend
.
So it's really not assured that the special "empty" buffer pointed-to will really be the same one eventually used to store a real string (though that buffer will also be guaranteed null-terminated in the same way). Certainly once you go above the capacity all bets are completely off, and do you really want to be tracking that event?
It really sounds like std::string
is not what you want, or that you should be accessing data()
as and when rather than calling it once then storing the resulting pointer.
If you're interacting with some C API that needs to store this pointer, I would switch to something else, perhaps a fixed-length character buffer. On the C++ side you can still make use of std::string_view
to do pleasing (read-only) things with your C-like character buffer, so you wouldn't lose any of that.
#include <string_view>
#include <iostream>
void OkayCApiStoreThisThen(const char*) {}
static constexpr const size_t BUF_SIZE = 255;
char silly_buf[BUF_SIZE] = {};
int main()
{
// Sadface
OkayCApiStoreThisThen(silly_buf);
// Happyface
std::string_view str(silly_buf, sizeof(silly_buf));
silly_buf[0] = 'l';
silly_buf[1] = 'o';
silly_buf[2] = 'l';
silly_buf[3] = '!';
std::cout << str.substr(0, 3) << '\n';
}
Upvotes: 3
Reputation: 1002
To have string::data()
returning char*
you have to upgrade to C++17, unfortunately. const_cast'ing result of data()
might be viable option if you really need to have write access to underlying data.
Upvotes: 4