使用operator []来操作std::string是否安全?

4

我正在处理一个旧的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 );

有没有更好的方法将数据传入字符串中?
2个回答

7
是的,至少在C++11中明确规定安全了。来自[string.require],重点是:

存储在basic_string对象中的char-like对象必须是连续的。也就是说,对于任何basic_string 对象s,对于所有n的值,都应该保持&*(s.begin() + n) == &*s.begin() + n,其中0 <= n < s.size()

这是DR 530的解决方案。在C++11之前,标准中没有明确规定,尽管实践中已经这样做了。

在C++14中,这个要求被移到[basic.string]中:

basic_string 是一个连续的容器 (23.2.1)。

其中[container.requirements.general]:

连续容器是指支持随机访问迭代器(24.2.7)并且其成员类型iteratorconst_iterator是连续迭代器(24.2.1)的容器。

其中[iterator.requirements.general]:

进一步满足以下要求的迭代器,对于整数值n和可解引用的迭代器值a(a+n)*(a+n)等效于*(addressof(*a)+n),称为连续迭代器


它是否也保证是以空字符结尾的或者不是? - MicroVirus
2
@MicroVirus 不是。不等式是< s.size(),而不是<=。(虽然在实践中,是的,不确定你如何实现常数时间的c_str() - Barry
请注意,由于我们已经在讨论C++11,现在也有一个方便的成员函数s.data(),这可能比老式的&s[0]更易读。 - ComicSansMS
2
@ComicSansMS 这个是 const 的。你可能把它和 std::vector 混淆了,后者有一个非 const 版本的 data() - user703016
@Cicada 令人惊讶的是,你说得对。我讨厌 std::string。 :D - ComicSansMS
显示剩余2条评论

2
问题在于代码是否
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[]定义如下:对于大小为0的非字符串s,在C++03中执行索引s[0]是未定义行为™。在C++11中,相应的段落§21.4.3/2则表示结果是“*(begin() + pos)如果pos < size(),否则是一个类型为T值为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] ) );
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接