原文链接:GitHub - Cpp-Club/Cxx_HOPL4_zh
HOPL 是 History of Programming Languages(编程语言历史)的缩写,是 ACM(Association of Computing Machines,国际计算机协会)旗下的一个会议,约每十五年举办一次。Bjarne 的这篇论文是他为 2021 年 HOPL IV 会议准备的论文,也是他的第三篇 HOPL 论文。在这三篇前后间隔近三十年的论文里,Bjarne 记录了 C++ 的完整历史,从 1979 年到 2020 年。这篇 HOPL4 论文尤其重要,因为它涵盖了 C++98 之后的所有 C++ 版本,从 C++11 直到 C++20。如果你对更早期的历史也感兴趣的话,则可以参考他的其他 HOPL 论文,及他在 1994 年出版的《C++ 语言的设计和演化》(The Design and Evolution of C++)。
鉴于这篇论文对于 C++ 从业者的重要性,全球 C++ 及系统软件技术大会的主办方 Boolan 组织了一群译者,把这篇重要论文翻译成了中文,让 C++ 开发人员对 C++ 的设计原则和历史有一个系统的了解。下面是论文的完整摘要:
到 2006 年时,C++ 已经在业界广泛使用了 20 年。它既包含了自 1970 年代初引入 C 语言以来一直没有改变的部分,又包含了在二十一世纪初仍很新颖的特性。从 2006 年到 2020 年,C++ 开发者人数从约 300 万增长到了约 450 万。在这段时期里,有新的编程模型涌现出来,有硬件架构的演变,有新的应用领域变得至关重要,也有好些语言在争夺主导地位,背后有雄厚的资金支持和专业的营销。C++——一种没有真正商业支持的、老得多的语言——是如何在这些挑战面前继续茁壮成长的?
本文重点关注 ISO C++ 标准在 2011 年、2014 年、2017 年和 2020 年的修订版中的重大变化。标准库在篇幅上约占 C++20 标准的四分之三,但本文的主要重点仍是语言特性和它们所支持的编程技术。
本文包含了长长的特性清单,其中记录了 C++ 的成长。我会对重要的技术要点进行讨论,并用简短的代码片段加以说明。此外,本文还展示了一些失败的提案,以及导致其失败的讨论。它提供了一个视角,如何看待这些年来令人眼花缭乱的事实和特性。我的重点是塑造语言的想法、人和流程。
讨论主题包括各种方向上的努力,包括:通过演进式变化保留 C++ 的本质,简化 C++ 的使用,改善对泛型编程的支持,更好地支持编译期编程,扩展对并发和并行编程的支持,以及保持对几十年前的代码的稳定支持。
ISO C++ 标准是通过一个共识流程演化而来的。无可避免,在方向、设计理念和原则方面,不同的提案间存在竞争和(通常是礼貌性的)冲突。委员会现在比以往任何时候都更大、更活跃,每年有多达 250 人参加三次为期一周的会议,还有更多的人以电子方式参加。我们试图(并不总是成功)减轻各种不良影响,包括“委员会设计”、官僚主义,以及对各种语言时尚的过度热衷。
具体的语言技术话题包括内存模型、并发并行、编译期计算、移动语义、异常、lambda 表达式和模块。要设计一种机制来指定模板对其参数的要求,既足够灵活和精确,又不会增加运行期开销,实践证明这很困难。设计“概念”来做到这一点的反复尝试可以追溯到 1980 年代,并触及到 C++ 和泛型编程的许多关键设计问题。
文中的描述基于个人对关键事件和设计决策的参与,并以 ISO C++ 标准委员会档案中的数千篇论文和数百份会议记录作为支持。
1. 前言
最初,我设计 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 标准委员会控制,它主要关注的是长长的新特性列表,以及对实际细节的关心。这是社区里最能表达和最有影响力的人所坚持的东西,仅仅基于哲学或理论观点就否认他们的关切和意见的话,恐怕就失之鲁莽了。
1.1 年表
为了给出一个快速的概述,这里有一个粗略的年表。如果你不熟悉 C++,很多术语、构件、库都会晦涩难懂;大多数在以前的 HOPL 论文 [Stroustrup 1993, 2007] 或本文中有详细解释。
- 1979 年:工作始于“带类的 C”,它变成了 C++;拥有了第一个非研究性的用户;
- 语言:
class
、构造函数/析构函数、public
/private
、简单继承、函数参数类型检查 - 库:
task
(协程和仿真支持)、用宏参数化的vector
- 语言:
- 1985 年:C++ 的首次商业发行;TC++PL1 [Stroustrup 1985b]
- 语言:
virtual
函数、运算符重载、引用、常量 - 库:
complex
算法,流输入输出
- 语言:
- 1989–91 年:ANSI 和 ISO 标准化开始;TC++PL2 [Stroustrup 1991]
- 语言:抽象类、多重继承、异常、模板
- 库:输入输出流(但没有
task
)
- 1998 年:C++98、第一个 ISO C++ 标准 [Koenig1998]、TC++PL3 [Stroustrup 1997]
- 语言:
namespace
、具名类型转换1、bool
、dynamic_cast
- 库:STL(容器和算法)、
string
、bitset
- 语言:
- 2011 年:C++11 [Becker 2011],TC++PL4 [Stroustrup 2013]
- 语言:内存模型、
auto
、范围for
、constexpr
、lambda 表达式、用户定义字面量…… - 库:
thread
和锁、future
、unique_ptr
、shared_ptr
、array
、时间和时钟、随机数、无序容器(哈希表)……
- 语言:内存模型、
- 2014 年:C++14 [du Toit 2014]
- 语言:泛型 lambda 表达式、
constexpr
函数中的局部变量、数字分隔符…… - 库:用户定义字面量……
- 语言:泛型 lambda 表达式、
- 2017 年:C++17 [Smith 2017]
- 语言:结构化绑定、变量模板、模板参数的构造函数推导……
- 库:文件系统、
scoped_lock
、shared_mutex
(读写锁)、any
、variant
、optional
、string_view
、并行算法……
- 2020 年:C++20 [Smith 2020]
- 语言:
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)。
1.2 概述
这篇论文是按照 ISO 标准发布的大致时间顺序组织的。
- §1:前言
- §2:背景:C++ 的 1979–2006
- §3:C++ 标准委员会
- §4:C++11:感觉像是门新语言
- §5:C++14:完成 C++11
- §6:概念
- §7:错误处理
- §8:C++17:大海迷航
- §9:C++20:方向之争
- §10:2020 年的 C++
- §11:回顾
如果一个主题跨越了一段较长的时间,比如“概念”和标准化流程,我会把它放在一个地方,让内容优先于时间顺序。
这篇论文特别长,真是一篇专题论文了。但是从 2006 年到 2020 年,C++ 经历了两次主要修订:C++11 和 C++20;而论文的早期读者们也都要求获得更多的信息。结果就是论文的页数几乎翻倍。即使以目前的篇幅,读者也会发现某些重要的主题没有得到充分的展现,如并发和标准库。