Is it safe to use operator [] for std :: string
I am struggling with the old c-style interface. I have a function with a signature like this:
/// if return_value == NULL only the length is returned
void f( char * return_value, size_t * size_only_known_at_runtime);
My question is, is the following code safe?
std::size required;
f( NULL, &required );
std::string s;
s.resize(required);
f( &s[0], &required );
Is there a better way to get data in a string?
source to share
Yes, it's safe, at least explicitly from C ++ 11. From [string.require], my kick is:
The char objects in the object
basic_string
must be stored contiguous . That is, for any basic_string of the object s, then for all values then
identifier&*(s.begin() + n) == &*s.begin() + n
for which0 <= n < s.size()
.
This was DR 530 . Until C ++ 11 this was not explicit in the standard, although it was done in practice anyway.
In C ++ 14, this requirement has moved to [basic.string]:
A
basic_string
is a continuous container (23.2.1).
where [container.requirements.general]:
A neighboring container is a container that supports random access iterators (24.2.7) and whose member types are
iterator
andconst_iterator
are continuous iterators (24.2.1).
where [iterator.requirements.general]:
Iterators that additionally satisfy the requirement that for integral values
n
and dereferenced values of an iteratora
and(a + n)
,*(a + n)
equivalently*(addressof(*a) + n)
, are called continuous iterators.
source to share
The question is, will the code
std::size_t required_size;
f( nullptr, &required_size );
std::string s;
s.resize( required_size );
f( &s[0], &required_size );
safe.
It depends on which C ++ standard is assumed, but since it is Undefined Behavior for case required_size
= 0 in C ++ 03 and C ++ 98, the general answer is no , it is not safe at all.
In C ++ 03, std::string
it was not formally guaranteed to have an adjacent buffer, but in practice all existing implementations had an adjacent buffer. Anyway, now after C ++ 11, where the standard included native buffer support, there will be no new C ++ 03 implementations with a non-contiguous buffer. Hence, this is not a problem.
The problem is rather that in C ++ 03 std::basic_string::operator[]
it was defined like this:
C ++ 03 §21.3.4 / 1 :
" Returns: If
pos < size()
, returnsdata()[pos]
. Otherwise, ifpos == size()
, versionconst
returnscharT()
. Behavior is undefined.
So, for a string s
of s
size 0, in C ++ 03 it was Undefined Behavior ™; for indexing s[0]
.
In C ++ 11, the corresponding paragraph §21.4.3 / 2 instead states that the result is " *(begin() + pos)
if pos < size()
, otherwise, a reference to an object of type T
with a value charT()
; the reference value is not changed."
Here's the code that works no matter which C ++ standard the compiler implements:
std::size_t required_size;
f( NULL, &required_size ); // Establish required buffer size.
if( required_size > 0 )
{
std::string s( required_size, '#' );
f( &s[0], &required_size );
s.resize( strlen( &s[0] ) );
}
source to share