每到这个时候,WG21 的工作人员总会遇到来自外界的疑问,“为什么 C++ 标准的发布时间这么严格?”。甚至许多新的委员会成员,他们对 WG21 的历史和这样做的原因同样并不了解。众所周知,WG21 有一个严格的时间表,按照这份时间表,机构每三年发布一次标准,从不会推迟。因此,在 7 月 5 日科隆大会之前的行政电话会议上,本文作者 Herb Sutter 在其他委员会主席的鼓励下,回应了采用这一方式的原因及背后的历史,该修订版在科隆大会的会议邮件中发布。本文是是面向感兴趣的公众发布的副本,希望能对你理解 C++ 标准的发布有所启发。

图0:为什么 C++ 标准每三年发布一次?

Q:在科隆大会之前,如果标准中有 Bug,WG21 会推迟 C++ 20 吗?

A:当然不会。我们正在按计划进行,修复 Bug 是最后一年的目标。这也是时间表在“19”(科纳)的早期冻结了 C++“20”的特性,留出一年的时间来修复 Bug 的原因,包括今年夏天开展一轮国际讨论。我们必须在 2020 年初(三个会议:科隆、贝尔法斯特和布拉格)采用评审反馈、任何其他问题的解决方案以及 Bug 修复。

Q:如果还有一两个会议,就可以添加几乎已经准备好的特性,那么 WG21 是否会推迟发布 C++ 20 呢?

A:当然不会。只能再等几次会议(布拉格大会之后),C++ 23 正式对外开放,可以将该特性作为 C++ 23 工作草案的第一项内容。例如,对于 concept,我们就是这样做的。它还没有完全准备好从 TS 直接进入 C++ 17,所以按照投票,其核心功能会在 C++ 20 的首次会议(多伦多)时加入 C++ 20 草案,留下足够的时间来完善,采用 TS 中余下的有争议的部分,稍微多花些时间准备(非“模板”的语法),并在第二年(圣地亚哥)采用。现在我们拥有了整个特性。

Q:这听起来太严格了。为什么我们每三年发布标准 ?

A:因为它是发布 C++ 标准仅有的两个项目管理选项之一,经验表明它比另一个选项更好。

Q:发布 C++ 标准的两个项目管理选项是什么?

图1:为什么 C++ 标准每三年发布一次?

A:对于发布目标,有两个基本选择:特性和发布时间。无论选择哪一个,都意味着放弃对另一项的控制。具体来说:

(1)“What”:选择特性。准备好了再发布,即放弃对发布时间的控制。

如果你发现标准草案中的某个特性需要更长的准备时间,那么就推迟到它完成。你可以使一个版本大到足以涵盖必要的开发时间,从而开发需要花费多年时间的重要特性,然后设法停止开发新特性,稳定这一版本。

这是 C++ 98(最初预计在 1994 年左右发布。Bjarne 最初表示,如果它不能在 1994 年前后发布,那么它将是一个失败)和 C++ 11(称为 0x,因为 x 预计在 7 左右)的模型。该模型“将病人公开”,时间的不确定导致了集成测试和发布延迟。这导致了巨大的市场不确定性,人们会想,委员会什么时候发布下一个标准,或者他们是否会发布(在社区中,实施者,甚至委员会内部的一些人严重怀疑在 1996 年和 2009 年我们是否会发布相应的版本)。在这段时间内,大多数编译器通常都是晚几年实现标准。因为没有人知道,委员会在修补版本时还会做多少不兼容的修改,或者什么时候发布,这导致社区可用的编译器对 C++ 的支持存在广泛的差别和分化。

为什么我们这样做,我们是傻瓜吗?不是。只是没有经验以及……“乐观态度”,心怀最大的善意想要打下良好的基础。在 1994 年 5 月 6 日,还有 2007 年 8 月 9 日,我们真的相信,如果延期到下次会议或者推迟两三次会议,我们就可以完成。但每次我们都最终推迟了四年,我们已经吸取了教训,没有所谓的推迟一两年。幸运的是,有了选项(2),这种情况已经改变。

(2)“When”:选择发布时间。交付准备好的特性,不去选择功能集。

如果你发现实现标准草案中的一个特性需要更多的准备时间,那么你就把它从此次发布中去掉,只交付准备好的东西。你仍可以跨越多个版本的开发时间开发重要特性,只要把它放到“分支”里去进行,并在准备就绪时把它们合并到主干标准。你一直都在开发特性,因为每一个特性的开发在准备好之前,都和具体的交付很好地解耦了。

本模型自 2012 年以来一直在使用,我们不想回去。该模型会定期“把病人关起来”,为了维持较高的质量,强制进行定期集成,在达到相当的稳定程度之前不合并到标准草案中,通常是在一个特性分支上。它还创建了一个可预测的交付周期,供行业参考和规划。在这段时间里,每次标准发布后,编译器越来越早地交付了符合标准的实现(这在以前从未发生过)。2020 年,我们希望多个完全符合标准的实现在同一年发布(这在以前从未发生过)。这对整个市场——实现者、用户、教育工作者以及所有人来说都是好事。

此外,请注意,由于我们采用了方式(2),我们还交付了更多(以大 / 中 / 小特性计数)质量更高的特性,而交付的是任何准备好的东西(如果没有准备好就推迟)。

Q:我们对(2)有多严肃?如果由一名重要的委员会成员所主导的主要特性“几乎准备好了”……我们会等待,不是吗?

A:不会。我们有历史数据:2016 年在杰克逊维尔,在 C++ 特性截止日,Bjarne Stroustrup 在全体会议上呼吁在 C++17 中包含 concept,而人们未能达成共识。因此,有人直接问他是否愿意让 C++ 17 推迟一年,加入 concept。Stroustrup 说“不”,没有任何的犹豫或迟疑,并补充说,没有 concept 的 C++ 17 比有 concept 的 C++ 18 甚或 C++ 19 更重要,尽管 Stroustrup 从事 concept 特性的开发已有 15 年。

真正的选择是:(2)发布没有 concept 的 C++ 17,然后在 C++ 20 包含 concept(我们就是这样做的)。或者(1)把 C++ 17 重命名为 C++ 20,这就等同于(2)跳过 C++ 17,并且不发布已经准备好的 C++ 17。

Q:介于(1)和(2)之间的东西呢,比如,基本上采用(2)的做法,但计划有“一点”灵活性,当我们觉得需要稳定一个特性时,就额外花“少许”时间?

A:不会,因为那就成(1)了。Fred Brooks 在《人月神话》中解释了“神秘的小偏差(mythical small slip)”,并得出结论:“避免小偏差。”

现在,想象一下,C++ 20 稍微延期了。现实情况是,我们将从(2)切换到(1),无论我们怎么试图否认它,都没有任何实际的好处。如果我们为了完整度而决定推迟 C++ 20,我们至少要将标准延期两年。一旦我们延期至少两年,C++ 20 会变为 C++ 22,甚至 C++ 23……但我们已经准备交付 C++ 23!所以不管是哪个计划,我们仍然是交付 C++ 23。唯一的区别在于,我们在此期间没有交付 C++ 20,完全准备就绪的工作多了许多,而世人则多等了三年。这没有理由,因为延迟不会造福那些未完全准备就绪的特性,大部分或全部都是如此。

所以,这个建议相当于“让我们把 C++ 20 变成 C++ 22 或 C++ 23”。而答案很简单,“是的,我们会有 C++ 23,但是在 C++ 20 之外,而不是取代 C++ 20。“推迟 C++ 20 实际上意味着跳过 C++ 20,而不是发布已经稳定而且准备就绪的工作,这样做没有好处。

Q:特性 X 被破坏了,需要时间修复,而 C++20 中剩余的修复时间不够用了,怎么办?

A:没问题!我们去掉它!在这种情况下,有人就需要写文章向 EWG 或 LEWG(酌情)说明问题,并提出从标准草案中去掉它的建议。这些组织就会考虑,如果他们确定该特性被破坏了(全体同意),这很好,该 C++ 特性将推迟到下一个版本。对于 C++ 0x 的 concept,我们已经这样做过。但是,按照计划(1),我们会延期,不仅那个特性,C++ 20 的整个特性集都会被纳入 C++ 23,那就太过分了。

Q:(2)意味着“大 / 小”版本吗?

A:不是。(2)仅仅意味着你不选择特性集,甚至是以“大 / 小”版本的粒度。简单地说,模型(2)是“准备好了就交付”。这使得发布具有以下特点:

①对于“较小”的特性,大小类似(通常为中等大小),因为它们的提前期往往比较短(每个小于 3 年),所以一般每次发布时我们看到的特性数量差不多;
②对于“较大”的特性,大小变化较大,它们需要更长的提前期(大于 3 年),每次标准发布时会使任何在标准时间窗口内已经成熟的特性做好合并准备,所以有时会比其他的版本多。

所以,C++ 14 和 C++ 17 相对较小,因为在此期间,许多标准化工作都是在提案文件(如 contract)和 TS“特性分支”(如 concept)的重要特性中进行的。

Q:那么,我们没有在 C++ 20 的三年周期中塞入很多东西吗?

A:没有。C++ 20 的变化比较大,不是因为我们在这三年做了更多的工作,而是因为许多重要项目(至少有两个自 2012 年以来就一直以它们当前的形式开展工作,它们是 P-proposals 和 TS“分支”)正好成熟,并且人们达成共识要在同一个发布周期内将其合并进草案。

开发重大特性需要多年的时间,这几乎一直是事实。C++ 98 和 C++ 11 采用的方案(1)和现在的方案(2)之间的主要区别在于,我们直到标准准备就绪才发布,现在,对于那些大特性,我们仍然是准备好再发布。但与此同时,我们也会发布其他准备好的特性,而不是完全停止任何发布。

C++ 20 与 C++ 14,C++ 17 的周期相同,都是 3 年。这不是说我们在这三年的时间里比前面的两个三年周期做得更多,只是有更多的重要特性要准备好合并。如果真的没准备好,我们可以再去掉它们,让它们为 C++ 23 做好准备。

事实上,我认为正确的思考方法是,C++14+17+20 作为一个整体是我们的第三个九年周期(2011-2020),在 C++ 98(1989-1998)和 C++ 11(2002-2011)之后。但是,因为我们现在采用方案(2),我们也会发布在第 3 年、第 6 年准备好的部分。

Q:在产品开发过程中发现 Bug 不是比在发布给客户之后发现更好吗?

A:当然。但如果我们谈论的是推迟发布 C++ 标准的理由,那么这个问题意味着两个错误的前提:(a)假设特性在标准尚未发布之前就已经发布和使用(已经有许多生产使用经验);(b)假设所有的特性都可以在标准发布之前一起使用(它们不能)。具体来说:

对于(a):本质上,C++ 20 最主要的特性至少在一个发布编译器中按照当前的标准草案形式实现了。实际上,在大多数情况下已经实际用于生产代码(已经发布给客户,而且客户非常满意)。例如,协程(截至本文写作的时候采用仅仅五个月)被用于 MSVC 生产代码已经两年,在 Clang 中使用已经至少一年,像 Azure、Facebook 等大规模使用的客户都非常满意。

对于(b):事实上,我们不会发现多少特性交互问题,直到用户在生产中使用它们,这通常意味着直到标准发布之后,因为实施者通常会等到标准发布之后才实现大多数东西。这就是为什么当我们对何时发布表现出任何的不确定性时,通常会发生的是,实施者会等待——他们将实现一些东西,但暂停实现整个东西,直到他们知道我们已经准备好。问下“最喜欢的编译器”团队,当他们在标准发布之前实现重要特性会发生什么。在许多情况下,他们必须实现不止一次,并不止一次地给客户造成破坏。因此,实施者等待委员会发布是合理的。

最后,不要忘记特性交互问题。除了在准备好时发布,发布后,我们也需要时间查找和修复特性之间交互的问题,添加特性交互支持。在这些新特性被广泛使用之前,我们通常都不知道这些交互。无论我们的标准推迟多久,都会有交互直到很久以后才被我们发现。关键是借助设计的灵活性来管理风险,以兼容的方式调整特性,而不是等到所有风险都消除。

Q:标准从来都不是完美的,难道我们不会把错误发布出去吗?

A:是的。如果我们看到一个特性还没准备好,我们应该去掉它。如果我们看到一个特性可以更好,但我们知道,修改可以用向后兼容的方式完成,这不是一个现在不发布它的理由,那可以在 C++ 的下一个版本中作为一个扩展。我们会特地发布计划进一步改进的特性,只要我们有足够的信心用向后兼容的方式完成。

Q:难道不应该以交付错误最小化为目标吗?

A:是的,我们应该以此为目标。然而,我们的目标不在于消除所有风险。不发布我们认为已经准备好的东西既有风险,也有(机会)成本。到目前为止,我们大部分时间都是正确的。

Q:你确定现在的质量比采用方案(1)时更好吗?

A:是的。借助客观的指标,特别是国家机构的评论和问题报告,C++ 14 和 C++ 17 一直是最稳定的版本。对于这些指标,它们每一个都比 C++ 98 或 C++ 11 好三至四倍。原因是我们定期发布,并优先将大特性放入 TS 分支(包括如何与主干标准整合的完整说明),在知道它们准备好之后将它们合并。

事实上,自 2012 年以来的核心标准始终保持“接近交付(near-ship-ready)”的状态(所以,即使是我们的工作草案,其质量也不亚于 C++ 98 和 C++ 11 标准)。在 2012 年之前,这从未发生过,我们常常“将病人公开”,问题列表很长,我们打算很快就安回去的器官散落在周围。如果我们想要,我们现在就可以交付 CD,没有科隆会议也可以,而且仍然比 C++ 98 或 C++ 11 的 CD 质量更高。考虑到 C++ 98 和 C++ 11 的成功,我们认识到,现在的质量比过去任何时候都要高,这意味着我们处于一个非常好的位置。

Q:(2)是否允许基于特性的目标,如P0592对于 C++next?

A:当然!只要它不包含“必须包括这些特性”这样的说法,因为这就成方案(1)了。瞄准一组特性,给予它们比其他特性更高的优先级,这就成了一个优先次序的问题。我们还是只接受准备好的特性,但我们肯定可以更有意识地优先考虑先做什么,这样它就有最好的机会尽快做好准备。

余下全文(1/3)

本文最初发表在www.infoq.cn,文章内容属作者个人观点,不代表本站立场。

分享这篇文章:

请关注我们:

发表评论

电子邮件地址不会被公开。 必填项已用*标注