acm*_*acm 114 c++ string stdstring copy-on-write c++11
据我所知,写时复制不是std::string
在C++ 11中实现符合性的可行方法,但最近在讨论中我发现自己无法直接支持该语句.
我是否正确C++ 11不承认基于COW的实现std::string
?
如果是这样,这个限制是否在新标准(其中)的某处明确说明了?
或者这个限制是否暗示,因为新要求的综合影响std::string
排除了基于COW的实施std::string
.在这种情况下,我会对"C++ 11有效禁止基于COW的std::string
实现" 的章节和样式推导感兴趣.
Dav*_*e S 116
这是不允许的,因为根据标准21.4.1 p6,只允许迭代器/引用的失效
- 作为任何标准库函数的参数,将非const basic_string作为参数引用.
- 调用非const成员函数,除了operator [],at,front,back,begin,rbegin,end和rend.
对于COW字符串,调用非const operator[]
将需要复制(并使引用无效),上面的段落不允许这样做.因此,在C++ 11中拥有COW字符串已不再合法.
Jon*_*ely 45
通过这些问题的答案戴夫小号和 gbjbaanb是正确的.(而Luc Danton也是正确的,虽然它更像是禁止COW字符串的副作用,而不是禁止它的原始规则.)
但为了澄清一些困惑,我将进一步阐述.各种评论链接到我在GCC bugzilla上的评论,其中给出了以下示例:
std::string s("str");
const char* p = s.data();
{
std::string s2(s);
(void) s[0];
}
std::cout << *p << '\n'; // p is dangling
Run Code Online (Sandbox Code Playgroud)
该示例的要点是说明为什么GCC的引用计数(COW)字符串在C++ 11中无效.C++ 11标准要求此代码正常工作.代码中的p
任何内容都不允许在C++ 11中失效.
使用GCC的老引用计数的std::string
实现,该代码是未定义行为,因为p
是无效的,成为一个悬摆指针.(接下来会发生的是,它在s2
构造时与它共享数据s
,但是获取非const引用通过s[0]
要求数据不被共享,s
"写入时复制"也是如此,因为引用s[0]
可能会被用来写入s
,然后s2
去超出范围,破坏指向的数组p
).
C++ 03标准明确允许 21.3 [lib.basic.string] p5中的行为,它表示在调用data()
第一次调用之后operator[]()
可能使指针,引用和迭代器无效.所以GCC的COW字符串是一个有效的C++ 03实现.
C++ 11标准不再允许这种行为,因为没有调用operator[]()
可能使指针,引用或迭代器无效,无论它们是否跟随调用data()
.
所以上面的例子必须在C++ 11 中工作,但不能与libstdc ++的COW字符串一起使用,因此在C++ 11中不允许使用那种COW字符串.
gbj*_*anb 19
它是,CoW是一种可以接受的制作更快字符串的机制......但......
它使多线程代码变慢(所有锁定以检查你是否是唯一一个在使用大量字符串时写入杀死性能的人).这是多年前CoW被杀的主要原因.
其他原因是[]
操作员将返回字符串数据,而不会保护您覆盖其他人希望不变的字符串.这同样适用于c_str()
和data()
.
快速谷歌说,多线程基本上是它被有效禁止(没有明确)的原因.
提案说:
提案
我们建议安全地同时执行所有迭代器和元素访问操作.
即使在顺序代码中,我们也在提高操作的稳定性.
此更改实际上不允许写入时复制实现.
其次是
由于远离写时复制实现而导致的最大潜在性能损失是具有非常大的读取主要字符串的应用程序的内存消耗增加.但是,我们认为对于那些应用而言,绳索是更好的技术解决方案,并且建议考虑将绳索建议包含在库TR2中.
绳索是STLPort和SGIs STL的一部分.
从21.4.2 basic_string构造函数和赋值运算符[string.cons]
basic_string(const basic_string<charT,traits,Allocator>& str);
[...]
2 效果:构造一个类对象,
basic_string
如表64所示.[...]
表64帮助文档通过此(复制)构造函数构造对象后,this->data()
具有以下值:
指向数组的已分配副本的第一个元素,其中第一个元素由str.data()指向
其他类似构造函数也有类似的要求.
basic_string
?关于
\n\n\n\n\n“ C++11 不承认基于 COW 的实现,我这样说对吗
\nstd::string
?
是的。
\n\n关于
\n\n\n\n\n如果是这样,这个限制是否在新标准的某个地方(哪里)明确规定?
\n
几乎直接地,由于许多操作的复杂性恒定,这需要在 COW 实现中对字符串数据进行 O( n ) 物理复制。
\n\n例如,对于成员函数
\n\nauto operator[](size_type pos) const -> const_reference;\nauto operator[](size_type pos) -> reference;\n
Run Code Online (Sandbox Code Playgroud)\n\n...在 COW 实现中,\xc2\xb9 都会触发字符串数据复制以取消共享字符串值,C++11 标准要求
\n\n C++11 \xc2\xa721.4.5/4 :\n\n\n\n\n” 复杂性:恒定的时间。
\n
…这排除了此类数据复制,因此排除了 COW。
\n\nC++03 支持 COW 实现,因为没有这些恒定的复杂性要求,并且在某些限制条件下,允许调用operator[]()
、at()
、begin()
、rbegin()
、end()
、 或rend()
使引用字符串项的引用、指针和迭代器无效,即可能导致COW 数据复制。C++11 中删除了此支持。
在撰写本文时被选为解决方案的另一个答案中,该答案得到了大力支持,因此显然被相信,它断言
\n\n\n\n\n”对于 COW 字符串,调用 non-
\nconst
operator[]
需要制作副本(并使引用无效),这是上面 [C++11 \xc2\xa721.4.1/6] 的 [quoted] 段落所不允许的。因此,在 C++11 中使用 COW 字符串不再合法。
这种说法在两个主要方面是不正确和具有误导性的:
\n\nconst
项目访问器需要触发 COW 数据复制。const
项目访问器也需要触发数据复制,因为它们允许客户端代码形成引用或指针(在 C++11 中)不允许稍后通过可以触发 COW 数据复制的操作使其失效。要了解正确的 C++11 COW 实现如何basic_string
工作,当忽略使其无效的 O(1) 要求时,请考虑一个字符串可以在所有权策略之间切换的实现。字符串实例以策略 Sharable 开始。如果此策略处于活动状态,则不能有外部项目引用。实例可以转换为 Unique 策略,并且当可能创建项目引用时(例如调用.c_str()
(至少如果生成指向内部缓冲区的指针),则必须这样做。在多个实例共享该值的所有权的一般情况下,这需要复制字符串数据。转换到 Unique 策略后,实例只能通过使所有引用无效的操作(例如分配)转换回 Sharable。
因此,虽然该答案的结论(即排除了 COW 字符串)是正确的,但所提供的推理是不正确的并且具有强烈的误导性。
\n\n我怀疑造成这种误解的原因是C++11的附录C中的一个非规范注释:
\n\n C++11 \xc2\xa7C.2.11 [diff.cpp03.strings],关于 \xc2\xa721.3: \n\n\n\n\n更改:
\nbasic_string
要求不再允许引用计数字符串
\n 理由:无效与引用计数字符串略有不同。此更改规范了本国际标准的行为(原文如此)。
\n 对原始功能的影响:有效的 C ++ 2003 代码在此国际标准中的执行方式可能不同
这里的基本原理解释了为什么决定删除 C++03 特殊 COW 支持的主要原因。这个理由,即为什么,并不是标准如何有效地禁止 COW 实施。该标准不允许通过 O(1) 要求使用 COW。
\n\n简而言之,C++11 失效规则并不排除std::basic_string
. 但他们确实排除了一种相当高效、不受限制的 C++03 风格的 COW 实现,例如至少一个 g++ 标准库实现中的实现。特殊的 C++03 COW 支持提高了实际效率,特别是使用const
项目访问器,但代价是微妙、复杂的无效规则:
\n\n\n”引用序列元素的引用、指针和迭代器可能会因该对象
\nbasic_string
的以下使用而失效: \n \xe2\x80\x94 作为非成员函数的参数(21.3.7.8)、(21.3. 7.9)和(21.3.7.9)。\n \xe2\x80\x94 作为 的参数。\n \xe2\x80\x94 调用和成员函数。\n \xe2\x80\x94 调用非成员函数,除了、、、、和。\n \xe2\x80\x94 除了返回迭代器的和形式之外,在上述任何使用之后,第一次调用非成员函数, , , , , 或。basic_string
swap()
operator>>()
getline()
basic_string::swap()
data()
c_str()
const
operator[]()
at()
begin()
rbegin()
end()
rend()
insert()
erase()
const
operator[]()
at()
begin()
rbegin()
end()
rend()
这些规则是如此复杂和微妙,以至于我怀疑许多程序员(如果有的话)能否给出精确的总结。我不能。
\n\noperator[]
如果忽略eg上的C++11恒定时间要求,那么COW forbasic_string
在技术上是可行的,但难以实现。
可以访问字符串内容而不引起 COW 数据复制的操作包括:
\n\n+
。<<
.basic_string
作为标准库函数的参数。后者是因为标准库允许依赖于特定于实现的知识和构造。
\n\n此外,实现可以提供各种非标准函数来访问字符串内容,而不触发 COW 数据复制。
\n\n一个主要的复杂因素是,在 C++11 中,basic_string
项访问必须触发数据复制(取消共享字符串数据),但要求不抛出,例如 C++11 \xc2\xa721.4.5/3 “抛出: Nothing。 ”。因此它不能使用普通的动态分配来创建新的缓冲区来进行COW数据复制。解决这个问题的一种方法是使用特殊的堆,可以在其中保留内存而不实际分配内存,然后为每个对字符串值的逻辑引用保留必要的数量。在这样的堆中保留和取消保留可以是常数时间 O(1),并且分配已经保留的数量可以是noexcept
。为了符合标准的要求,使用这种方法似乎每个不同的分配器都需要一个这样的特殊的基于保留的堆。
\n注意:
\n\xc2\xb9const
项目访问器触发 COW 数据复制,因为它允许客户端代码获取数据的引用或指针,不允许通过例如非-const
项目访问器。\n
归档时间: |
|
查看次数: |
24695 次 |
最近记录: |