为什么Allocator::reference正在逐步淘汰?

29
我看了一下std::vector的规范,注意到C++03中reference typedef从Allocator::reference改变为C++11中的value_type&。我感到很惊讶,于是我开始深入研究。
在C++03 §20.1.5 [lib.allocator.requirements]中有一个表32,其中定义了X::referenceT&,而X::const_reference被定义为T const&
然而,在C++11 §17.6.3.5 [allocator.requirements]中,缺少referenceconst_reference
接下来我们有C++11中添加的§20.6.8 std::allocator_traits,它不包括reference。但是§20.6.9 std::allocator包括。
最后,§23.2.1 [container.requirements.general]定义X::reference为"lvalue of T",X::const_reference被定义为"const lvalue of T"。
因此,我谷歌搜索并找到了这篇论文(1, 2),建议从分配器要求中删除reference,但它没有提到任何背后的理由。但也有一个LWG问题反对这种改变。
此外,我还发现了与Alexander Stepanov的采访(链接),他谈到了reference如何封装机器特定的内存布局,以及Herb Sutter的文章(链接),其中他谈到了指向容器元素的指针、容器要求以及std::vector<bool>不是容器。
那么,你对所有这些有什么看法?reference有用吗?它是否达到了它的目的? "花式"引用如何适应标准?这是完全消除它们、使容器要求更加严格并废弃std::vector<bool>的大胆举动吗?

7
精心研究的问题 - David
不,这并不是非常大胆的做法。如果有人尝试实例化std::vector<bool>,我会中断构建。 - BЈовић
3个回答

6
因为嵌套的typedef是多余的。《Effective STL》一书(作者Scott Meyers),第49页:标准明确允许库实现者假定每个分配器的指针typedef是T*的同义词,每个分配器的引用typedef与T&相同。

我所链接的论文实际上建议将此从标准中删除。 - user3624760
它有可能是一个破坏性的变化,所以他们可能会小心谨慎地处理,从向量和类似的引用中解开。尽管如此,我支持从标准中消除任何/所有歧义。 - Scott Jones

5

http://en.wikipedia.org/wiki/Allocator_(C%2B%2B)

"最初,它们被设计为使库更加灵活和独立于底层内存模型,允许程序员使用自定义指针和引用类型与库一起使用。但是,在将STL采纳为C++标准的过程中,C++标准化委员会意识到完全抽象化内存模型将产生无法接受的性能损失。为了解决这个问题,对分配器的要求进行了更严格的限制。因此,分配器提供的定制水平比Stepanov最初设想的要有限得多。"

最初,它们被设计为抽象出内存本身,允许通过互联网连接在其他计算机上分配内存,并使用指针/引用来跟踪数据。类似地,可以在纯C ++中创建像Java一样的GC。这种抽象看起来像是一个惊人的想法!

然而,这会导致当时认为无法接受的性能损失。此外,如果你仔细思考一下,就会发现这几乎是不可能在代码中工作的。每个void func(const string&)都必须变成template<class allocator> void func(allocator::reference),这是一个无法推断的上下文,因此您必须在函数调用中显式编写分配器(func<std::allocator<std::string>::const_reference>(username)),而没有人会这样做,这将导致GC不能正常工作。现在,分配器仅抽象出内存分配/释放。


很遗憾现在没有这个选项了,因为我正在精确地建模一个系统,在模拟器上运行代码时,我想要“抽象掉内存本身”,模拟器会模拟总线访问。但是我也希望代码使用std容器,并且可重用,以便在使用std分配器的情况下也能正常工作。 - mattgately
1
@mattgately:迭代器对于这个仍然“大多数情况下”有效。 - Mooing Duck

2
与Alexander Stepanov的采访中,他提到在将STL添加到标准库的提案中,有人要求他从内存模型中创建一个抽象层。因此,分配器应运而生。在LWG问题中,有一个实现示例,其中自定义分配器的reference被定义为T __far&

但由于我没有那么多时间去搜索原因未知,所以C++03标准在§20.1.5 p4中有以下文字:

本国际标准中描述的容器实现允许假定其分配器模板参数满足表32中所列条件之外的以下两个附加要求。 - 给定分配器类型的所有实例必须可互换,并始终相等。 - 必须将typedef成员指针、const_pointer、size_type和difference_type分别设置为T*、T const*、size_t和ptrdiff_t。
这有效地破坏了自定义内存模型分配器与标准容器互操作的能力。
在搜索所有提到单词“allocator”的C++11之前的论文时,我发现有一个主要共识,即从标准中删除这些单词。最终,this paper建议使用以下评论删除它们: “我们asel words已经消失了。举起你的杯子,干杯。”
胜利?我们终于可以任意使用内存模型了吗?并不完全是这样。同一篇论文还建议从分配器要求中删除reference等内容。而且看起来已被投票纳入了标准。

我之前提到的LWG问题反对这种改变,但是它被以下声明关闭:

没有达成一致意见来进行改变

因此看起来分配器的原始目的在今天并不那么重要。这是维基百科的说法:

当前分配器的目的是为程序员提供容器内存分配的控制,而不是适应底层硬件的地址模型。实际上,修订后的标准取消了分配器代表C++地址模型扩展的能力,正式(并故意)消除了它们的原始目的。

最后,Container::reference与分配器无关。它是为了允许代理集合实际上不是容器而创建的。因此,它将保留下来。顺便说一句,这似乎是标准中最终的措辞违背了最初的意图的另一个例子。


1
由于未知原因 - Mooing Duck

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