当前位置:编程学习 > C/C++ >>

C++之父Bjarne谈C++中的STL模板

 在1994年,我主要关心的是如何使ISO C++标准尽可能地好--同时在它所包含的特性和规范的质量两个方面--并获得多数人的同意。即使人们不接受某种规范,也不会影响它(规范)的良好性。ISO标准没有强制力,因此有些人认为自己不值得浪费时间来适应它,除非(群体)社团的压力能够使他们确信该规范的价值。对于一个实现者来说,适应环境是很重要的额外工作,因此适应环境是一个有意识的决定,并且需要分配一些资源,而这些资源本来可以在其它地方使用。某些晦涩的语言特性很难在某些编译器中实现。我们可以实现或者购买类库,而且领先的、可靠的实现者(implementer)也有机会用自己的富于想像力的专利特性来"锁定"用户。因此,我认为要点是:让委员会成员和他们所代表的组织确信该标准的文档是他们所期望看到的最好的文档。

  在做了很多工作之后,该委员会获得了成功。1997年10月,在Morristown(New Jersey,USA)会议上,技术成员的最终投票结果是43-0。在获知这个结果以后,我们进行了庆祝活动!在1998年,ISO成员国以空前的22-0的投票结果批准了这个标准。为了获取大家的一致同意,委员会做了大量的技术工作,也使用了一些外交策略:在那个时候,我喜欢说"易做图问题无法解决;我们必须找到引发该问题的技术问题并解决它"。我无法想象仅仅通过投票,因为少数服从多数才简单"解决"的问题,同时,由于"易做图上的讨价还价"的问题也危害了我们最好的技术判断--而这个问题(模板的分开编译)仍然在"恶化",需要寻找一个更好的技术方案。

  在最后投票之前的一年里,委员会的工作是:

  1. 细节、细节和更多的细节。

  2. STL

  3. 模板的分开编译

  第一个问题非常明显:国际标准必须用大量的篇幅来关注细节信息;实际上,实现(implement)与已有标准的兼容性是标准的关键目标,同时还是实现之间的工具和应用程序能够迁移的基础。标准是一个712页的文档(加上索引等内容),它是采用高度技术化的和正式的方式编写的,因此为了理解真正的含义需要很多的细节信息。像以前一样,我在新语言规范上附加了新版的"C++编程语言",以提供更有帮助意义和面向用户的语言描述。

  STL的出现

  第二个问题,STL("标准模板类库",它是ISO C++标准类库的容器和算法框架)成为标准的一部分是一个主要的创新,并且它成为了新的用以思考已经出现的编程技术的出发点。STL基本上易做图性地脱离了我们原来思考容器和容器使用问题的方式。在Simula早期,容器(例如列表)曾经是困扰人的:如果,并且只有当某个对象已经(显式或隐式地)衍生自那些包含编译器所需链接信息的特定的"Link"或"Object"类的时候,它才能被放到容器中。这种容器基本上是引用Link的容器。这暗示着基本的类型(例如int和double)不能直接地放入容器中,数组类型(它直接支持基本的类型)必定跟其它的容器不同。此外,如果我们希望把真正的简单类对象(例如complex和Point)放入容器中,那么它们在时间和空间上就无法达到理想效果。它同时还暗示着这种容器不是静态类型安全的。例如,Circle可以被加入列表中,但是当它被提取出来的时候,我们只知道它是一个Object,需要使用一个转换(显式类型转换)来恢复其静态类型。

  Simula容器和数组关于内建和用户定义类型(只有后来的一些可以放入容器)、关于容器和数组(只有数组能够保存基本的类型;数组不能保存用户定义类型,只能保存用户定义类型的指针)都有一些奇怪的条款。Smalltalk使用了相同的方法,也有相同的问题,后来的一些语言(例如Java和C#)也是这样的。由于它有明显的效用,而且很多设计者现在都熟悉它,所以很多C++类库也遵循这种模型。但是,我却发现它是无规律的和低效率的(在时间和空间上),使用它开发真正通用的类库是不可以接受的。这就是我在1985年没有为C++提供适当的标准类库(这个失误)的根本原因。

  当我编写D&E的时候,我知道了一种容器和容器使用的新方法,它是由Alex Stepanov开发的。Alex当时在HP实验室工作,之前在Bell实验室工作了多年,在那儿他接近了Andrew Koenig,我也在那儿与他讨论过类库设计和模板机制。他鼓励我进一步研究某些模板机制的泛化和效率,但是很幸运的是,他却没有说服我让模板更类似Ada泛型。如果他成功了,他就无法设计和实现STL了!

  在1993年末,Alex在泛型编程技术方面显示了他最近十年的长期研究的进展,这种技术是基于严格的数学基础的、目标是成为"最通用和最高效"的编程技术。他是一个容器和算法的框架。他首先联系了Andrew,Andrew花几天时间研究这种技术之后,就把它展示给我了。我的第一反映是很迷惑。我发现STL的容器和容器使用方式是多余的,甚至于是丑陋的。与很多通晓面向对象编程的程序员一样,我认为自己知道容器的样子与STL代码的样子有怎样的不同。但是,在我建立工具列表(我认为这个列表对于容器来说是很重要的)的几年里,令我惊讶的是,我发现STL除了一个条件之外,符合其它所有的条件。那个缺少的条件是使用通用基类为所有的衍生类(例如所有的对象或容器)提供服务(例如永续性)。但是,我不认为这种服务对容器的概念有本质的意义。

  它让我花了几周时间才感觉到STL比较"舒适"。在那以后,我担心把一个全新样式的类库介绍给C++群体已经太晚了。让标准委员会在标准进行过程中这么迟的时候接受新的和易做图性的东西的成功几率是非常小的(的确是这样的)。即使出现最好的情况,标准也会被延迟一年--而C++群体急切需要该标准。同时,委员会本质上是一个保守的团体,而STL是易做图性的。

  因此成功的机会是很渺茫的,但是我还是在它上面进行着辛勤的工作。毕竟,由于C++没有足够大的、足够好的标准库,我的感觉非常糟糕。Andrew Koenig尽了最大的努力来鼓励我,并且Alex Stepanov用他知道的最好的东西来游说Andy和我。幸运的是,Alex不太了解在委员会中使某种东西成为主流的难度,因此他不太沮丧,并且继续研究技术方面,继续为Andrew和我讲授。我开始给其他人解释STL背后的想法。

  1993年10月,在加利福尼亚的San Jose举行的标准委员会会议上,我们邀请了Alex进行晚间演讲。"它叫做C++编程的科学,它处理了规则类型的大多数原则--连接构造、赋值和等同。我还介绍了转发迭代子的原则。我没有提起任何容器的问题,只提到一个算法:查找"。这次演讲是活跃的下层社会的大胆创新,而其巨大的乐趣使委员会的态度从"现在让我们作些主要的事情"变成了"等等,让我们瞧一瞧"。

  这正是我们需要的"暂停时间"!在后来的四个月中,我们进行试验、争论、游说、讲授、编程和重新设计,这样Alex才能在1994年三月加利福尼亚的San Diego委员会会议上提供STL的完整说明。1994年末在HP一个会议上,Alex提出了C++类库实现,我们在很多细节上达成了一致,但是STL的大小成为了主要的障碍。最后,在Alex的催促下,我拿起笔,逐字地删除,大约删掉了三分之二的内容。对于每一个工具,我都必须向Alex和其他类库专家非常简短地解释为什么它不能被删掉、为什么它使大多数C++程序员受益。这是个可怕的过程。Alex后来声明说这让他心痛。但是,这样的删减造就了现在知名的STL,并让它1994年10月在加拿大Waterloo的会议上成为ISO C++标准的一部分--而原始的和完全的STL都没有达到这个目标。对"简化STL"的必要修改也把标准延迟了一年以上。回想起来,我认为当时我们做的事情造成的伤害比预料的要小一些。

  在对采用STL的可能性的讨论中,我记得一件事情:Beman Dawes冷静地声明自己认为STL对于普通程序员来说过于复杂了,但是作为试验,他自己实现了10%,从此他不再认为STL超过了标准的难度。Beman是委员会中很少编写应用程序的人。不幸的是,委员会趋向于由建立编译器、类库和工具的人员所控制。

  在STL方面我信任Alex Stepanov。在STL之前,他花了十年以上的时间,用一些无用的语言(例如Scheme和Ada)来研究基本的想法和技术。但是,Alex第一个坚持要求其他人一起参与。David Musser和Alex在泛型编程方面一起工作了约二十年,Meng Lee与他一起在HP工作,帮助他编写最初的STL。Alex和Andrew Koenig之间的电子邮件讨论也有帮助作用。除了苛刻的试验之外,我的贡献很小。我建议与内存相关的所有信息都集中到一个对象中--就形成了分配器(allocator)。我还草拟了Alex想法的初始需求表,建立表格记录STL算法和类对它们的模板参数的要求。这些需求表实际上表明这种语言的表达能力不足--这种需求应该成为代码的一部分。

 1.1 STL的理念

  那么什么是STL呢?到目前为止,它作为标准C++的一部分已经快十年了,因此你的确应该知道它,但是如果你不熟悉现代的C++,那么我就有必要对它的想法和语言使用方式作一些简单的介绍。

  我们来考虑一个问题:把对象存储在容器中,并编写算法来操作这些对象。我们按照直接、独立和概念的混合表现方式来考虑这个问题。自然地,我们希望能够在多种容器(例如列表、向量、映射)中存储多种类型(例如整型、Point、Shape)的对象,并在容器中的对象上应用大量的算法(例如排序、检索、积聚)。此外,我们希望使用的这些对象、容器和算法都是静态类型安全的、尽可能地快速、尽可能地简洁、不冗长、易于阅读。同时实现这些目标并不容易。实际上,为了解决这个难题,我差不多花费了十年还是没有找到成功的方法。


  STL解决方案是以带元素类型的参数化容器以及与容器完全分离的算法为基础的。容器的每种类型都提供一种迭代子(iterator)类型,只使用这些迭代子就可以实现对容器中所有元素的访问。通过这种方式,我们可以编写算法来使用迭代子而不用知道提供迭代子的容器的信息。每种迭代子类型与其它类型都是完全独立的,除非要为必要的操作(例如*和++)提供了相同语义(semantics)。我们可以用图
补充:软件开发 , C++ ,
CopyRight © 2022 站长资源库 编程知识问答 zzzyk.com All Rights Reserved
部分文章来自网络,