std::string需要将其字符存储在连续的内存块中吗?

24

我知道在C++98中,std::basic_string<>std::vector<>都不需要使用连续的存储。当时人们认为这是一个失误,但针对std::vector<>的问题很快就被指出并在C++03版本上修复了。

我记得曾经阅读过关于在C++11还称为C++0x时讨论要求std::basic_string<>使用连续存储的文章,但由于我没有密切关注那时的讨论,并且在工作中我仍然受限于C++03,所以我不确定其结果。

那么,std::basic_string<>是否需要使用连续存储?(如果需要,那么哪个版本的标准首先要求它?)

如果你想知道:如果你的代码将&str[0]的结果传递给期望写入连续内存块的函数,这一点非常重要。(我知道str.data(),但由于明显的原因,旧代码不使用它。)


好的,既然 str.data() 需要是常数时间并且需要返回一个连续的内存块,我会得出结论,是的。 - Šimon Tóth
1
请参考以下类似的问题:https://dev59.com/9nI-5IYBdhLWcg3wKVA9 和 https://dev59.com/C3E95IYBdhLWcg3wkeuq。这些问题回答了你的疑问,但是有些难以理解。 - slaphappy
1
@Mr.kbok:我已经看到了它们两个,读了答案,认为我更想要一个最新的明确答案。 - sbi
1
@Angew:在C++03中,str.data()返回一个const char* - sbi
1
就我所知,我从未见过或听说过不连续的std::basic_string实现;对此感兴趣的任何人都可能将其添加为独立的STL rope类似物。因此,在实践中,如果我是你,我会先查看你的工作C++03的标准库(如果有偏执症),然后在假定它始终是连续的情况下编码。 - Tony Delroy
显示剩余6条评论
3个回答

29

C++11标准中,basic_string 21.4.1.5规定:

basic_string对象中的char-like对象必须被连续存储。也就是说,对于任何basic_string对象s,当0 <= n < s.size()时,&*(s.begin() + n) == &*s.begin() + n的身份应该保持一致。


15
在C++03标准中,不能保证字符串的元素是连续存储的。[basic.string]如下所示:
  1. 对于charT这样的char-like类型,类模板basic_string描述了可以存储由可变数量的任意char-like对象(第21条款)组成的序列的对象。序列的第一个元素位于位置零。如果给定的char-like类型在上下文中清晰可见,则称这样的序列为“字符串”。在本条款的其余部分中,charT表示这样的给定char-like类型。通过作为模板参数传递的Allocator类,basic_string类成员函数分配并释放字符串的存储空间,Allocator::value_type应与charT相同。
  2. 基本字符串的类模板符合Sequence的要求,如(23.1.1)所规定。此外,由basic_string支持的迭代器是随机访问迭代器(24.1.5),basic_string符合Reversible Container的要求,如(23.1)所规定。
  3. 在所有情况下,size() <= capacity()。
然后在C++17标准中,更改如下:
  1. 类模板basic_string描述了对象,该对象可以存储由可变数量的任意char-like对象组成的序列,该序列的第一个元素位于位置零。如果它所持有的char-like对象的类型在上下文中清晰可见,则称这样的序列为“字符串”。在本条款的其余部分中,指定存储在basic_string对象中的char-like对象的类型为charT。
  2. basic_string的成员函数使用作为模板参数传递的Allocator类的对象来分配和释放包含的char-like对象的存储空间。
  3. basic_string是一个连续的容器(23.2.1)。
在所有情况下,size()<= capacity()。在C++17之前不能保证,但现在可以。由于std :: string :: data所施加的限制,这种非保证几乎是无用的,因为调用std :: string :: data会给您一个字符串中字符的连续数组。因此,除非实现是按需且以恒定时间执行此操作,否则字符串将是连续的。 如果您的代码将&str [0]的结果传递给期望写入连续内存块的函数,则这一点非常重要。operator []的行为也已更改。在C++03中,我们有:如果pos < size(),则返回数据()[pos]。否则,如果pos == size(),const版本返回charT()。否则,行为是未定义的。因此,仅当s为空时,保证了具有定义行为的const版本,如果尝试&s [0]。在C++11中,它们将其更改为:如果pos < size(),则返回*(begin()+ pos)。否则,返回类型为charT的对象的引用,该对象的值为charT(),其中修改对象会导致未定义的行为。因此,现在如果尝试&s [0],不管是const还是非const版本,都具有定义行为,当s为空时。

3
即使在C++03中也无法使string::data()成为一个常数时间函数,它返回一个连续的序列而不需要实际存储是连续的。 - Šimon Tóth
标准规定 string::data() 必须包含与字符串内容相同的内容,并对 string::data() 设置恒定时间要求。没有办法拥有像 deque 一样的结构并保持恒定时间限制。 - Šimon Tóth
@Let_Me_Be,我在答案中加入了关于string::data()的警告。 - NathanOliver
感谢您对Rahul的回答进行了扩展。作为C++的偶尔访问者,我不得不绕过流行的概括:其中之一是基于指针的语言不提供有关任何非平凡数据结构的物理布局的保证:您所知道的只是该结构中的数据将从其指针透明地访问。透明地,而不是“通过对地址执行算术运算顺序访问”。因此,早期规范“符合...可逆容器”而不是“连续的”-您可以确信假设连续块曾经咬伤过某个人。 - Nigel Heffernan
@Nathan:在过去的十年中,我一直遇到这样的代码,人们将&s[0](或&*str.begin())传递给需要指向可写C字符数组的第一个元素的函数。通常情况下,我会让这样的代码保持不变(当然,假设他们确保字符串不为空),因为从来没有任何已知的实现使字符串具有非连续内存,并且正如我所说,我似乎记得这是一个要求。然而,我现在想知道是否最终已经进行了这种更改。谢谢! - sbi
显示剩余2条评论

2
根据草案标准 N4527 21.4/3 类模板 basic_string [basic.string]

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


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