对 std::string 使用运算符 [] 是否安全

Is it safe to use operator [] for std::string

我正在与旧的 c 风格界面作斗争。我有一个带有这样签名的函数:

 /// if return_value == NULL only the length is returned
 void f( char * return_value, size_t * size_only_known_at_runtime); 

我的问题是,下面的代码安全吗?

std::size required;
f( NULL, &required );
std::string s;
s.resize(required);
f( &s[0], &required );

是否有更好的方法将数据放入字符串中?

是的,它是安全的,至少在 C++11 中是这样。来自 [string.require],强调我的:

The char-like objects in a basic_string object shall be stored contiguously. That is, for any basic_string object s, the identity &*(s.begin() + n) == &*s.begin() + n shall hold for all values of n such that 0 <= n < s.size().

这是 DR 530 的决议。在 C++11 之前,这在标准中并不明确,尽管它在实践中已经完成。

在 C++14 中,此要求已移至 [basic.string]:

A basic_string is a contiguous container (23.2.1).

其中 [container.requirements.general]:

A contiguous container is a container that supports random access iterators (24.2.7) and whose member types iterator and const_iterator are contiguous iterators (24.2.1).

其中 [iterator.requirements.general]:

Iterators that further satisfy the requirement that, for integral values n and dereferenceable iterator values a and (a + n), *(a + n) is equivalent to *(addressof(*a) + n), are called contiguous iterators.

问题是代码是否

std::size_t required_size;
f( nullptr, &required_size );
std::string s;
s.resize( required_size );
f( &s[0], &required_size );

安全。

这取决于假设的 C++ 标准,但由于在 C++03 和 C++98 中 required_size = 0 的情况是未定义的行为,一般答案是 没有,总的来说不安全


在 C++03 中,std::string 没有正式保证有一个连续的缓冲区,但实际上所有现存的实现都有连续的缓冲区。无论如何,现在在C++11之后,连续缓冲区保证被正式纳入标准,不会出现任何新的非连续缓冲区的C++03实现。所以这不是问题。

问题是在 C++03 中 std::basic_string::operator[] 定义如下:

C++03 §21.3.4/1

Returns: If pos < size(), returns data()[pos]. Otherwise, if pos == size(), the const version returns charT(). Otherwise, the behavior is undefined.

因此,对于大小为 0 的非 const 字符串 s,在 C++03 中,Undefined Behavior™ 进行索引 s[0].

在 C++11 中,相应的段落 §21.4.3/2 说结果是“*(begin() + pos) if pos < size(),否则引用类型为 [=25= 的对象” ] 的值为 charT();参考值不得修改。”


无论编译器实现哪种 C++ 标准,下面的代码都有效:

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] ) );
}