据我所知,写时复制不是在C ++ 11中实现符合std::string
的可行方法,但最近在讨论中我发现自己无法直接支持言。
我是否认为C ++ 11不承认基于COW的std::string
实现?
如果是这样,这个限制是否在新标准(其中)的某处明确说明了?
或者这个限制是否暗示,因为它是std::string
的新要求的组合效果,排除了基于COW的std::string
实施。在这种情况下,我会对“C ++ 11有效禁止基于COW的std::string
实现”的章节和样式推导感兴趣。
答案 0 :(得分:112)
这是不允许的,因为根据标准21.4.1 p6,迭代器/引用的失效只允许
- 作为参考的任何标准库函数的参数 将非const basic_string作为参数。
- 调用非常量 成员函数,除了operator [],at,front,back,begin,rbegin, 结束,并撕裂。
对于COW字符串,调用非const operator[]
将需要复制(并使引用无效),上面的段落不允许这样做。因此,在C ++ 11中使用COW字符串已不再合法。
答案 1 :(得分:42)
Dave S和gbjbaanb的答案正确。 (而Luc Danton也是正确的,虽然它更像是禁止COW字符串的副作用,而不是禁止它的原始规则。)
但为了澄清一些困惑,我将进一步阐述。各种评论链接到a comment of mine on the 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
该示例的要点是说明为什么GCC的引用计数(COW)字符串在C ++ 11中无效。 C ++ 11标准要求此代码正常工作。代码中的任何内容都不允许{C} 11中p
无效。
使用GCC旧的引用计数std::string
实现,该代码具有未定义的行为,因为p
无效,成为悬空指针。 (当构建s2
时,它会与s
共享数据,但通过s[0]
获取非const引用需要取消共享数据,因此s
执行“写入时复制”,因为引用s[0]
可能用于写入s
,然后s2
超出范围,破坏p
指向的数组)。
C ++ 03标准在21.3 [lib.basic.string] p5中明确允许该行为,其中表示在调用data()
之后第一次调用{{ 1}}可能使指针,引用和迭代器无效。所以GCC的COW字符串是一个有效的C ++ 03实现。
C ++ 11标准不再允许这种行为,因为对operator[]()
的调用可能会使指针,引用或迭代器无效,无论它们是否跟随{{1}的调用}}
因此上面的示例必须在C ++ 11中工作,但不使用libstdc ++的COW字符串,因此不允许使用那种COW字符串C ++ 11
答案 2 :(得分:17)
是的,CoW是一种可以接受的制作更快字符串的机制......但是......
它使多线程代码变慢(所有锁定以检查你是否是唯一一个在使用大量字符串时写入杀死性能的代码)。这是多年前CoW被杀的主要原因。
其他原因是[]
运算符会返回字符串数据,而不会保护您覆盖别人期望不变的字符串。这同样适用于c_str()
和data()
。
快速谷歌说多线程基本上是reason it was effectively disallowed(未明确)。
提案说:
提案
我们建议安全地进行所有迭代器和元素访问操作 同时可执行。
即使在顺序代码中,我们也在提高操作的稳定性。
此更改有效地禁止了写时复制实现。
接着是
由于切换而导致的最大潜在性能损失 copy-on-write实现是增加的内存消耗 对于具有非常大的读取主要字符串的应用程序但是,我们 相信对于那些应用绳索是更好的技术 解决方案,并建议考虑列入一个绳索建议 图书馆TR2。
Ropes是STLPort和SGIs STL的一部分。
答案 3 :(得分:5)
来自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()
指向
其他类似构造函数也有类似的要求。
答案 4 :(得分:1)
由于现在保证字符串是连续存储的,现在允许您指向字符串的内部存储器(即&amp; str [0]的工作方式与数组相同),这是不可能的做一个有用的COW实现。你需要复制太多的东西。即使只在非const字符串上使用operator[]
或begin()
也需要副本。
答案 5 :(得分:0)
我一直想知道不可变的奶牛:一旦创建了奶牛,我只能通过从另一头奶牛分配而进行更改,因此它将符合标准。
我今天有时间尝试进行一个简单的比较测试:一个大小为N的映射,该映射由字符串/牛作为键,每个节点都包含映射中所有字符串的集合(我们有NxN个对象)。
在字符串大小约为300字节且N = 2000的情况下,母牛的速度稍快一些,并且使用的内存几乎减少了一个数量级。参见下文,大小以kbs为单位,b代表奶牛。
~/icow$ ./tst 2000
preparation a
run
done a: time-delta=6 mem-delta=1563276
preparation b
run
done a: time-delta=3 mem-delta=186384