咨询培训 著译作品关于荣耀 Bjarne Stroustrup 网络资源 九炉堂日志联系首页

Bjarne Stroustrup

C++的过去、现在和未来

荣耀 张成 译

www.royaloo.com

System Design Frontier (SDF): 任何语言都利用思维背后的表达力,不管是简练的,还是繁琐的。写作是一个形式化的过程,编程亦然,只不过编程并非无拘无束的。当您设计一种语言时(比如C++),您认为语法和语义哪个更重要?以C++为例,语言设计和实现的关键差异是什么?

Bjarne Stroustrup (BS): 编程语言设计的目的是允许在代码中直接表达想法。这种表达应该优雅并能生成高效的代码。理想情况下,语法直接对应于语义。这听起来有些荒谬,因为理论上可以自由选择从语法到语义的映射。然而,程序员关于某种形式的语法“应当具有的含义”有着强烈的预期(这是建立在他们的经验之上的)。例如,他们“已经知道”+应该表示加法。同样的道理——虽然没有大量的应用支持这种观点——他们“知道”琐细的、易键入的、或隐含的特性,不应该有明显的运行时开销。

此外,“相似的”语法应该映射到相似的语义。可以说,程序员的许多期望都不尽合理且不一致。其中很多源于某些早期语言的设计瑕疵,只不过这些瑕疵变得家喻户晓、为人接受而已。然而,当设计一个新的语言特性或新库时,对这类问题的考虑是非常重要的。而且,对于程序员的反应而言,什么是对真正有价值的原则的合理反应,什么又纯粹是迷信,也很难判断。语法问题通常都很荒谬。只要看看C语言中反复出现的语法怪事(甚至尚未被声称为C兼容性问题)就明白了。

语言的设计和实现之间并没有什么关系。有大量的实现技术和工具可以用来实现语言设计目标。通常历史因素和兼容性的限制使得问题变得棘手。例如,一种新的解析器技术如果不能处理现有的代码和语义我们就不能使用它。然而,我们基本上知道如何将可以表达的任何语言语义映射到任何机器。

C++的一个有趣且重要的方面是从语言特性到机器原语有着直接的映射。例如,一个char映射到一个byte,一个int映射到一个寄存器,一个double映射到一个浮点寄存器,并且C++的基本操作直接映射到机器指令。除此之外,C++的抽象机制通过简单的组合映射到硬件。透明性(以及可预计的性能)是C++被广泛应用于嵌入式系统编程的原因之一。参见我的ICESS'04论文“Abstraction and the C++ machine model”(http://www.research.att.com/~bs/abstraction-and-machine.pdf)。

SDF: 在您创建C++之前,您持有学生签证在剑桥大学学习,凭着工作签证开始在贝尔实验室的工作,请问您从一个国家迁移到另一个国家时感受到文化冲击吗?这对您在贝尔实验室创建C++有何影响?

BS: 是的,每一次都是重大的文化冲击。这种冲击在美国持续的时间比英国还长,要以年而不是星期来计算。也许是因为年轻的学生比拖家带口的稍微年长点的男人更灵活罢了,但我怀疑英美两国之间的文化差异要远比丹麦和英国之间的差异大。当我初到英国时,我下了一番功夫努力理解别人所表达的意思,我知道这种差异对我来说还是蛮大的。我的英语还行,但它属于校园英语,而不是普通英国人说的英语。

这也许可以间接联系到语言设计。我必须努力理解人们和文化。我有意识地用一种本地人所不使用的方式表达。这迫使我清晰地阐明我的理解。我也不得不解释当地的文化与我的祖国(丹麦)文化的异同点。我想这是对了解工作场所的文化、组织以及个体背景的很好的锻炼。直到我定居美国并工作了一段时间后,我也很少想当然,还是习惯于向他人解释其实很简单的事情(他们认为的“简单和自然”与我认为的不同)。这些对语言设计的技术部分并无甚帮助,然而毋庸置疑的是,这对于理解问题和阐明解决问题的方法很有助益。大多数实用科学(生活常识)与工程学是相通的。

SDF: 我们知道您喜欢历史、哲学、文学作品等等,在您创建C++的过程中,这些爱好有无帮助作用呢?在C++诞生期间,有没有一些快乐和悲伤愿意与我们分享?

BS: 我认为阅读历史使我变得有耐心,并且不会试图强迫一切事情恰如自己所愿。它让我对于合理追求的限度有着很好的判断,并对一些更具煽动性的技术抱着强烈的反感。

SDF: 自然语言,比如英文或中文,不需要标准化,然而在大多数情况下编程语言确实需要标准化,或者可以称之为受约束的编程。显然,C++起初是作为一种事实上的标准语言,后来演变为一种标准化的语言。自由软件运动和开源运动对C++的演化有何影响?假如以不受约束的语言或自然语言编程的话,从受约束的编程到自由编程有多远的距离?

BS: 嗯,法国人(以及其他一些国家的人)对自然语言标准化的需求可能会持有不同的意见。所有语言都有关于拼写、语法和发音的最低限度的标准。如果偏离规范太远,你就有无法被他人所理解甚至遭到社会的反对的危险。

对我从事的工作领域而言,我认为用接近于自然语言的方式进行编程的想法是不恰当的。没错,我们是希望灵活一些,但还是要比自然语言有着更详细、更精确的规定。好的编程语言就是精心设计的人工语言,但是不应该含有自然语言中每条语句都可能蕴含的模棱两可的意思。作为比较,我真的不愿意讨论没有方程式的广义相对论,英语无法胜任表达许多物理学领域的基本思想。汉语以及其他自然语言也不能胜任描述主要的计算机系统的任务。从另一方面来说,我也不会试图在C++中描述莎士比亚:-)

总的来说,开源软件运动对C++ ISO的标准化发挥了极大的促进作用。各种不同的商业公司一致认为,除非对C++语言的样子达成共识,并确保没有人把它作为政治或商业博弈筹码,否则我们将只能得到毫无价值的东西。标准化是一项艰巨的任务,C++标准委员会的志愿者理应得到更多的信任和支持。通过他们的努力,今天的C++比1990年的要好很多,并且C++0x将会比现在的C++还要好。

SDF: 在C++编程中,通常首先设计类规范,这是以面向对象观点将动态的现实世界中一系列快照进行静态抽象(从动态到静态)的结果,经过编码、调试和测试后,再动态地模拟现实世界的同样快照(从静态到动态)。从面向对象的角度来看, C++如何帮助程序员实现从动态到静态的映射,以及随后的静态向动态的映射?

BS: 如果我有机会命名自己最喜欢的程序风格,这将是“面向类的编程”,但我并不擅长寻找时髦的名字。我所属于的思想流派源于Simula和相关的设计理念,即强调编译期检查,灵活的(静态)类型系统。对程序的行为的推理植根于源代码的(静态)结构。重点应放在异常处理保证、不变式等,这些都是与静态的结构密不可分的。这是我知道的处理正确性问题的惟一有效的方式。测试是不可或缺的,但如果没有良好的内部程序结构,测试必然是非系统化的且不完备的。对于任何重要的系统而言,简单的黑盒测试是不可行的,因为程序状态呈指数级增长。

因此,我建议人们考虑类不变式、异常处理保证、高度结构化的资源管理等。我还要补充一点,我极其不喜欢调试(因为这是特殊的、非系统性的劳动),我偏爱对源代码和系统测试进行推理。我还对各种运行时的代码修改活动持有健康的怀疑态度(诸如自我修改代码、动态链接、插件以及运行时补丁等),因为这些往往回避了仔细的代码分析和整体性的系统测试。

SDF: 在《C++程序设计语言》中,您宣称C++是一个偏向系统编程的通用编程语言,支持封装、继承、模板、重载等数据抽象机制,同时重载(包括操作符和函数)可以被视作控制抽象,你设想过其他控制抽象机制以更好地支持面向对象编程吗?

BS: 在很多上下文中,“控制抽象”不是一个定义良好的术语。我非常乐意看到并发(以很多形式)得到C++的支持。我期望在C++0x中会看到线程库和futures支持(在此上下文中,一个“future”是一个线程结果的占位符,是一种易用的同步形式)。目前有很多人讨论将“lambdas”用于C++(在此上下文中,lambda表达式是一个可以传递到某处以执行一个操作的对象,需要使用被调用者和调用者的上下文的信息)。

顺便提一句,你可以在这儿找到C++0x的所有提案:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/ 。从我的publications网页则可以找到一些易懂的讨论:http://www.research.att.com/~bs/papers.html。

SDF: 在《C++程序设计语言》中,您宣称C++是一种偏向系统编程的通用编程语言,它支持面向对象编程,而面向对象编程是为了更好的支持软件复用。如您所知,因为可度量性、粒度和接口等棘手的问题,软件复用是很难实现的。那么,从C++的角度看,解决软件复用问题的最佳复用粒度是什么?

BS: 我不认为我将复用当作面向对象编程的理由。我通常将复用视作优秀、灵活的设计及高效实现的结果。

最容易“复用”的“东西”是巨大的和微小的组件,即整个应用程序(例如浏览器或编译器)和简单的函数(例如sqrt())。 Unix允许人们以进程的形式复用完整的程序。最难办的复用粒度是中等规模的东西,例如类、类层次结构和库。

C++主要依靠复用源代码和对象代码库(静态或动态链接),它不提供自己的“组件”概念,而是依赖于所运行的平台。这显然有利(可以使用每一个主机系统的设施)有弊(使用了特定平台设施的代码很少能被移植)。在此领域C++最大的弱项是,在一些平台上缺乏C++ ABI和动态链接库的概念。后者可能由C++0x补救,而前者则依靠编译器厂商,然而他们至今在这方面没有彼此合作的兴趣。

C++在此领域的强项是它所支持的丰富编程风格提供的灵活性。当考虑复用时,面向对象编程和泛型编程的组合应用威力强大。抽象类提供精确的接口,这些接口可以很容易地被单独编译的代码所匹配。模板则提供了面向对象技术所无法提供的灵活组合能力和性能。C++0x将显著地改善对泛型编程的支持。concept将既能解决与模板代码检查有关的主要问题,又能进一步提高灵活性和易用性。在此上下文中,concept是一个语言构造,它表达一个类型、一套类型或一套类型及一些整数所必需的属性。参见C++0x概览。如果你有兴趣,可以阅读一篇技术性的OOPSLA'06论文,即Douglas Gregor等人写的“Concepts: Linguistic Support for Generic Programming in C++”(http://www.research.att.com/〜bs/oopsla06.pdf)。

SDF: 作为C++最初的设计者,从您自己的角度看,C++对于现今主流应用而言存在什么优势和劣势(经过在不同行业中运用大约20年后)?

BS: 优势:灵活性,通用性,性能,可移植性,良好的工具支持,除了C之外比其他竞争者能用于更多的平台,访问硬件和系统资源的能力,对程序员和设计师而言良好的可用性。劣势:复杂性,因劣质教育和荒诞的传言而导致的不理想的使用状况。

毋庸置疑,C++并不完美——没有哪一种语言是完美的,只是C++常常被贬低有点儿让人奇怪。不妨看一看这儿的应用清单:http://www.research.att.com/~bs/applications.html。C++的成功是因为它具有独一无二的语言特性组合,可能更重要的是,与其它语言相比,C++在很多领域没有致命的缺陷。对于C++应用的任何一个领域,你都能找到一个简单的替代品,但在另一个领域,这种“简单的替代品”则会导致严重的问题。C++中有不少瑕疵,但鲜有致命的瑕疵。

纵观信息行业30年,我很不情愿给“主流”下一个狭窄或精确的定义。如你所知,一向被忽略的一些领域可能会成为瞩目的焦点。想一想从大型主机到PC的变迁以及互联网的出现。每一次变迁都使得针对“之前的主流”专门化的语言入土为安,而通用、可移植、灵活的语言则胜出。在这样的转变中效率亦非常关键,因为系统依赖于高级组件的效率,而专门化则意味着这种高级组件定位有误。

SDF: 从C到C++的迁移过程中,C++迅速普及的原因之一是,编程范型从命令式(过程式)编程转变为面向对象编程。随着时间的推移,可能会诞生一种新的具有支配地位的编程范型,C++将会升级以迎合新的编程范型吗,或者会出现一种崭新的语言取代C++?

BS: 从长远来看,毋庸置疑C++将会被取代,希望是某种更好的语言。然而,俗话说,“从长远来看,我们都会去世”。在很长一段时间内C++仍将大有作为。我从来都没把C++仅看成一门面向对象语言。我一直都打算让它支持多种编程范型。参见“Why C++ isn't just an Object-Oriented Programming Language”。(OOPSLA'95,http://www.research.att.com/~bs/oopsla.pdf)

很多人抱怨C++发展的不够快,还有很多人抱怨C++发展的太快。这些迹象表明C++的发展基本上是正常的。我希望C++语言特性的演化能更快一些,特别是更快采纳一些主要的C++库。然而,没有人能够得到所有想要的东西,这其实很好,因为个人的意愿(无论多有经验)如果过于狭隘,就很容易变成社群的负担。

稳定对现实世界中的代码至关重要。如果没有保持兼容性的负担,我们可以得到更好的语言来编写新式的代码,然而这对现实项目来说用处不大。我们需要的语言既要能够应对新的挑战,又要能够维持旧代码和支持该语言的基础设施的正确性。许多C++编译器仍然具有“ARM兼容性”,能够编译1989年的老式代码(这还意味着支持1985年的老式代码,即针对第一个商业C++实现编写的代码)。

显而易见,对兼容性和稳定性的考虑减缓了C++的演化进程,并使新特性的设计和实现变得复杂化。然而,自从1985年以来C++就一直稳步推进,现在也不会停止。C++0x被规划为C++09。而且,没有理由认为C++0x会是C++演化的终点。除了稳定性外,稳定的注入新思想对一个活跃、多产的用户社群是很有必要的。

-完-