最初,我设计 C++ 是为了回答这样的一个问题:如何直接操作硬件,同时又支持高效、高级的抽象?C++ 在 1980 年代仅仅是一个基于 C 和 Simula 语言功能的组合,在当时的计算机上作为系统编程的相对简单的解决方案,经过多年的发展,已经成长为一个远比当年更复杂和有效的工具,应用极其广泛。它保持了如下两方面的关注:
这种组合是 C++ 区别于大多数语言的决定性特征。“零开销”是这样解释的 [Stroustrup 1994]:
抽象在代码中体现为函数、类、模板、概念和别名。
C++ 是一种活的语言,因此它会不断变化以应对新出现的挑战和演变中的使用风格。2006 年至 2020 年期间的这些挑战和变化是本文的重点。当然,一门语言本身不会改变;是人们改变了它。所以这也是参与 C++ 演化的人们的故事,他们识别出面临的挑战,诠释解决方案的局限,组织他们的工作成果,并解决他们之间必然出现的分歧。当我呈现一种语言或标准库特性时,其背景是 C++ 的一般发展和当时参与者的关切。对于在早期被接受的许多特性,我们现在从大量的工业使用中获得了后见之明。
C++ 主要是一种工业语言,一种构建系统的工具。对于用户来说,C++ 不仅仅是一种由规范定义的语言;它是由许多部分组成的工具集的一部分:
只要有可能,只要合适,我就会考虑这些组成部分之间的相互作用。
有一种流传广泛的谬见,就是程序员希望他们的语言是简单的。当你不得不学习一门新的语言、不得不设计一门编程课程、或是在学术论文中描述一门语言时,追求简单显然是实情。对于这样的用途,让语言干净地体现一些明确的原则是一个明显的优势,也是理想情况。当开发人员的焦点从学习转移到交付和维护重要的应用程序时,他们的需求从简单转移到全面的支持、稳定性(兼容性)和熟悉度。人们总是混淆熟悉度和简单,如果可以选择的话,他们更倾向于熟悉度而不是简单。
看待 C++ 的一种方式是,把它看成几十年来三种相互矛盾的要求的结果:
我添加了感叹号,因为这些观点的表达常常带着不小的情绪。
我想让简单的事情简单做,并确保复杂的事情并非不可能或没有必要地难。前者对于不是语言律师的开发者来说是必不可少的;后者对于基础性代码的实现者是必要的。稳定是所有意图持续运行几十年的系统的基本属性,然而一种活的语言必须适应不断变化的世界。
C++ 有一些总体构想。我阐述了一些(如《C++ 语言的设计和演化》(The Design and Evolution of C++)[Stroustrup 1994](§2)、设计原则(§9.1),以及 C++ 模型(§11.1))并试图让语言在演化时遵循它们。然而,C++ 的开发由 ISO 标准委员会控制,它主要关注的是长长的新特性列表,以及对实际细节的关心。这是社区里最能表达和最有影响力的人所坚持的东西,仅仅基于哲学或理论观点就否认他们的关切和意见的话,恐怕就失之鲁莽了。
为了给出一个快速的概述,这里有一个粗略的年表。如果你不熟悉 C++,很多术语、构造、库都会晦涩难懂;大多数在以前的 HOPL 论文 [Stroustrup 1993, 2007] 或本文中有详细解释。
class
、构造函数/析构函数、public
/private
、简单继承、函数参数类型检查task
(协程和仿真支持)、用宏参数化的 vector
virtual
函数、运算符重载、引用、常量complex
算法,流输入输出task
)namespace
、具名类型转换[^1]、bool
、dynamic_cast
string
、bitset
auto
、范围 for
、constexpr
、lambda 表达式、用户定义字面量……thread
和锁、future
、unique_ptr
、shared_ptr
、array
、时间和时钟、随机数、无序容器(哈希表)……constexpr
函数中的局部变量、数字分隔符……scoped_lock
、shared_mutex
(读写锁)、any
、variant
、optional
、string_view
、并行算法……concept
、module
、协程、三路比较、改进对编译期计算的支持……span
、格式、改进的并发和并行支持……请注意,早年 C++ 的库是很匮乏的。事实上,当时还是存在大量各种各样的库(包括图形用户界面库),但很少被广泛使用,并且很多库是专有软件。这是在开源开发普及之前的事。这造成了 C++ 社区没有一个重要的共享基础库。在我的 HOPL2 论文 [Stroustrup 1993] 的回顾中,我认为那是早期 C++ 最糟糕的错误。
任务库 [Stroustrup 1985a,c] 是一个基于协程的库,支持事件驱动的仿真(例如随机数生成),与替代方案相比是非常高效的,甚至可以运行在很小的计算机上。例如,我在 256KB 的内存中运行了 700 个任务的仿真。任务库在 C++ 早期非常重要,是贝尔实验室和其他地方许多重要应用的基础。然而,它有点丑陋,并且不容易移植到 Sun 的 SPARC 体系结构,因此大多数 1989 年以后的实现都不支持它。2020 年,协程才刚刚回归(§9.3.2)。
总的来说,C++ 的特性不断增多。ISO 委员会也废除了一些特性,对语言进行了稍许清理,但是考虑到 C++ 的大量使用(数十亿行代码),重要的特性是永远不会被移除的。稳定性也是 C++ 的关键特性。要解决跟语言不断增长的规模和复杂性相关的问题,办法之一是通过编码指南(§10.6)。
这篇论文是按照 ISO 标准发布的大致时间顺序组织的。
如果一个主题跨越了一段较长的时间,比如“概念”和标准化流程,我会把它放在一个地方,让内容优先于时间顺序。
这篇论文特别长,真是一篇专题论文了。但是从 2006 年到 2020 年,C++ 经历了两次主要修订:C++11 和 C++20;而论文的早期读者们也都要求获得更多的信息。结果就是论文的页数几乎翻倍。即使以目前的篇幅,读者也会发现某些重要的主题没有得到充分的展现,如并发和标准库。
[^1]: 译注:即新的、非 C 风格的类型转换