译自:
Bjarne Stroustrup
2009年7月在德国Frankfurt召开的会议上,C++标准委员会通过投票决定从C++0x中移除Concepts。那些已经为Concepts工作了很多年的人,以及知道Concept潜在能力的人,听到这个消息会感到十分失望,不过,这个决定不会影响大部分C++程序员。与C++98相比,C++0x在现实的程序世界里依然更有表现力和效率。委员会移除Concepts是为了控制风险和保证进度。也许5年后会提供一个改进后更强大的Concepts。本文记录了移除Concept的原因,简要记录了导致委员会这么做的争论和风险,提供一些参考给喜欢研究Concepts的人,并且指出(与广为流传的谣言相反)C++的”天不会蹋“。
没有Concepts的C++0x2009年7月在Frankfurt召开的(WG21)上,用来限定模板参数需求的机制Concepts被标记为“去除“(更直接的说是“淘汰“)。就是说,Concepts不会在C++0x以及标准库里出现。这件事——在我看来——是C++一次重大的倒退,但并不是灾难;如果不这样做,可能会更糟。
我已经围绕Concepts本身工作了七年,而之前为了得到这个解决方案,花了更长的时间。其他围绕Concepts工作的人也花了差不多同样长的时间。比如(按时间排序):
- Bjarne Stroustrup和Gabriel Dos Reis:,2003年十月。最早讨论在C++引入concepts的设计原则。
- Bjarne Stroustrup:,2003年十月。讨论concept检测的模型。
- Bjarne Stroustrup和Gabriel Dos Reis:,2005年四月。试图统一基于(和其他需求)N1510,N1522和N1536的Concept设计。
- Jeremy Siek,Douglas Gregor,Ronald Garcia,Jeremiah Willcock,Jaakko Jarvi和Andrew Lumsdaine:。N1758==05-0018,2005年五月。
- Gabriel Dos Reis和Bjarne Stroustrup:。POPL06。2006年一月。
- D. Gregor,B. Stroustrup:. N2042==06-0012。2006年6月。C++0x concepts未来工作的基础。
- Douglas Gregor,Jaakko Jarvi,Jeremy Siek,Bjarne Stroustrup,Gabriel Dos Reis,Andrew Lumsdaine:。 OOPSLA'06(国际面向对象编程会议),2006年十月。关于C++0x设计和实验性编译器“ConceptGCC“的一篇学术论文。
- 在Frankfurt会议前的工作论文(含有concepts的C++语言和标准库): . N2914=09-0104,2009年六月。
- B. Stroustrup:, N2906=09-0096。2009年六月。
不用说,我和其他人都很失望。其他选择会更糟的事实很让人心寒,而且我也没法给出快速简单的补救措施。
需要注意的是,C++0x对特性的改进不会对大部分程序员和直接使用者灶成影响。有了对并行编程的支持,更好的标准库,以及其他会使使用者写出(在效率和维护上)更好代码的改进,C++0x依旧比C++98更有表现力。特别是,所有我曾经给出的C++0x的例子(比如,在ACM 上的里的例子),只要不包含关键字“concepts“和”requires“,都不受影响。也可以参考我维护的。一些人甚至很欢迎这个改动,因为这使C++变得比他们期盼的还简单。
concepts曾经是为了让模板的使用有更好的理论基础而提出的重要新特性,可以使标准库的定义更严谨,而且也是让泛型编程更容易让大众使用的重要部分。不过现在,只能在没有语言支持的情况下使用concepts。在我看来,最理想的未来是,今后五年有更好的概念来代替concepts。这需要一些人为此努力工作(但不是“由委员会来设计”)。
发生了什么?concepts,在经过过去多年的发展并于2008年接受进入C++0x工作论文后,引入了很多技术上的妥协(这些妥协是合乎常理且必须的。)实验性的实现已经足够测试concepts化的标准库,但是还没达到能成为产品的质量。质量问题令一些人很担心,但是我个人认为这些测试已经足够证明concepts本身了。
我关心的是如何设计Concepts,特别是concepts在平均水平的程序员中的易用性。这些心得会在一些委员会成员中分享。concepts的既定目标是让广大的程序员更容易使用泛型编程[],但在我看来,这只是我目标的一部分:不仅仅让泛型编程更易用,concepts将会(只)成为专家手里另一个有力的工具。在过去半年左右的时间里,我从用户的角度重新审视了C++0x,我担心即便是使用concepts化的库,依旧会给程序员增加额外负担。我觉得目前concepts的设计和在标准库中的应用,并没有恰当体现出这些年在concepts上的经验。
几个月后,Alisdair Meredith(一个来自UK的富有洞察力的委员)和Howard Hinnant(标准库工作组的头头)问了一些问题,这些问题是关于谁应该直接使用concepts的哪个特性,以及如何使用。这导致了一场关于易用性的讨论,有很多人发表了自己的看法和观点。最终,在经过复杂的讨论后,我公开了我的结论[]。
简而言之,我的观点是:
- concepts目前的定义难于使用,而且很容易导致误用concepts,误用模板,所以也许还不足以被C++0x接纳。
- 一组小的简化可以在现有的C++0x进度表上,提供足够使用(good-enough-to-ship)的concepts,或者可能只遗漏一些小功能。
这个证据实在太强了(应该指从C++0x移出concepts的证据)。请记住,委员会的讨论一般都是很友善的,而且我们的目的是达成一致,因此会尽量避免直接与不同意见对峙。不幸的是,这场决定未来的(内部)讨论规模庞大(数以百计的详杂信息)且杂乱不堪。我们无法就具体讨论什么问题(如果确实有问题的话)和如何讨论达成一致。这使我在Frankfurt展示了几个其他的解决方案:
- “解决并发布”
- 遗留工作:移除显式“concepts“,加入显式精化(explicit refinement),加入“concepts“/类型匹配,处理“concepts“映射范围的问题。
- 风险:没有实现,描述复杂。
- 时间表:没有改变,或者再有一个会议。
- "约定并发布"
- 遗留工作:约定(核心和标准库)
- 风险:旧模板的问题依旧存在,让“革新”社区失望(“消耗了7年的工作“)
- 时间表:在concepts上再有五年的工作(需要全部重新设计)或者没有时间表。
- "维持现状"
- 遗留工作:细节。
- 风险:不能接受的编程模型,复杂的描述(另外的观点:没有)
- 时间表:没改变
我和一些人更喜欢第一种方案(“解决并发布”),并认为这个方案是可行的。不过,委员会里大部分人选择了第二种方案("约定并发布" ,改叫“移除”)。在我看来,这两种方案都好于第三种(“维持现状”)。“Concepts“的支持者里有一部分人也投票反对在C++0x里加入concepts,我对这类投票的解释是,整个加入concepts这个想法似乎会引起一些争论,而这些争论会影响到C++0x的进度(在我看来,这些对concepts的职责是不公平的),而且有些人对concepts根本没有兴趣。有鉴于此,”解决并发布“不是一个现实的选择。实际上,所有支承concepts的特性描述,都被“推迟”或者“终止”了。我警告过如果现在移除concepts,必然要等很久才能重新继续探讨这个特性,因为不再有进度的压力,基本上需要重新评估所有设计。
很令人惊讶的是(可能是吧),在Frankfurt上没有任何关于concepts的技术演讲和讨论。讨论都集中在计划时间,而且我印象里所有投票都优先考虑的是时间因素。
不过并不能批评委员会过于谨慎。这并不是“Bjarne与委员会对拼“,讨论的目的是试图平衡很多重要的事情。我和其他一些人对没有机会“解决并发布“concepts感到失望,但C++并不是一门学院性质的实验语言。除非其他成员认为新特性对已有产品代码产生破坏的风险很低,不然他们一定会反对。总体上说,委员会对数亿行代码负责。比如,如果concepts得不到C++0x的支持,或者已有concepts但长时间依旧使用无约束的模板,会导致C++社区分裂成很多子社区。因此,一个不完善的concepts比不提供concepts更糟糕。如果要在这两个间选择,我也会选择移除concepts。我宁愿选择受挫,而不是灾难。
技术问题concepts没有解决的问题主要是如何区别显式映射和隐式映射。(参见[]):
- 当有concepts的限定需求时,一个类型需要匹配concepts,是使用自动类型匹配(比如,一个提供了带有适当参数的+-*/的类型X,是否应当自动匹配到需求常见运算符的concept C?),还是需要一个额外的显式声明(一个从X到C的concept映射)来确定具体的匹配?(我的答案:在绝大部分情况里使用自动匹配。)
- 是否应该在使用自动concepts和显式concepts间提供某种选择?是否设计者可以强制所有使用者遵从他的选择?(我的答案:所有的concepts都应该自动匹配)
- 如果一个类型X提供了成员函数X::begin(),这个成员应该自动匹配到一个需求begin(T)的concept C,还是需要用户提供一个从T到C的concept映射?一个例子是std::vector和std::Range。(我的答案是:应该自动匹配)
对“在Frankfurt维持现状“的回应,都和我的建议相左。看来我必须简化我的解释并且提供足够的细节和理由。
我不能把整个讨论都重复一遍,不过我的结论是:在“维持现状”的设计里,concepts映射有两个作用:
- 通过增加/映射某些属性,把类型映射到concepts,
- 判断一个类型是否能匹配一个concept。
不过,第二种情况在某些人眼里更重要,而不是把它当作某种不合适的少见的需求。当两个concepts在语义上不同时,需要的不是判断一个类型可以匹配其中一个而不能匹配其他(这个——在最好的情况下,也只是一个变通方案——其实是对基本问题的一种间接且精心的攻击),而是某个类型是否满足某个concepts的语义而不满足其他的语义(满足成为一个axiom(s)的语义而不是其他的语义)。
举个例子,STL的input迭代器和forward迭代器在语法上有个关键的差异:你可以从一个forward迭代器开始做两次遍历,而input迭代器却不行。比如,在需要多次遍历的算法里最好不要使用流迭代器。“维持现状”的解决方法是强制每个使用者声明哪个类型匹配到forward迭代器,哪个类型匹配到input迭代器。我希望增加的内容:当(且仅当)你的类型想表达的语法对两个concept来说都是不自然的,而编译器无法判断哪个concepts更适合你的类型时,你必须明确表达你希望的语法:比如,“我的类型支持多次遍历语法”。有人可能说:“当你所拥有的只是concept映射时,所有东西看起来都需要一个类型/concept断言。“
在Frankfurt的会议上,我总结:
不同的人有不同的看法和侧重。无论如何,从高层次上说,这个回答可能有点不够清晰——但是不会引起太大的争论。所有合理的concept半成品设计都提供了上述特性。
我个人更关心可编程性(易用性,通用性,易学性,可扩展性),正式规范的复杂性(40页的标准文本)是第二位的。其他人更关心编译时间和运行时间。不过,我认为实验性的实现(ConceptGCC [])表明,受约束的模板(使用concepts)和无约束模板的运行速度一样快,甚至更快些。ConceptsGCC本身真是够慢的,但我想这并不重要。当其验证完所有想法后,我们就可以摆脱现在的困境。最简单的说,现在的困境是:
- “不要在没有商业化的实现前进行标准化”
- “主流厂商不会在没有标准的情况下实现某个特性”
不论怎样,细节设计和实验性的实现间,必须达成某种方式的妥协。
我对concepts的原则是:
- 鸭子类型(典故:看上去像鸭子,走起来像鸭子,还会“嘎嘎”叫的那个就可以当作鸭子)
- 让模板在泛型编程中成功的关键(与其他特性对比,比如带接口的面相对象)
- 可替代性
- 在没有比“保证”更强的先决条件下,决不调用某个函数。
- “偶然匹配“只是很小的问题"
我在Frankfurt之前的工作论文里展示的对concepts的“微小修正”:
- concepts是隐式/自动的
- 明确细化
- 一般的concept映射可见性
- 最小化“实现泄漏“(应该是指某个编译单元的映射会在别的编译单元里起意外作用)
- 简单的类型/concept匹配
- 将vector看成一个range,而不需要多余的concept映射
更多的细节,参见[]。
没有C++0x了,C++1x长存即便移除了concepts,下一代C++标准依然会推迟。坏消息是,不会有C++0x了(除非你把C++03这种修正标准算进来)。我们只能等C++1x,并且期盼x是个小数字。好消息是,C++1x已经确认了所有特性(除非某个国际标准组织强烈坚持要在正式标准里加入某些特性)。剩下的,就是大量解决重大技术问题和意见的工作了。
特性列表和讨论可以在我的里找到。这里给出一部分:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- (thread_local)
-
- syntax and semantics
-
还有库:
Improvements to
; see unordered_map
; see shared_ptr, weak_ptr, and unique_ptr
即便没有concepts,与C++98相比,C++1x也会有很大进步,尤其是当你认为这些特性(还有其他的)是为了协调最大化表达能力和灵活性而设计的。我希望我们能在大概5年后的C++修订版本中看到concepts。也许我们可以把那个版本称作C++1y甚至是C++y!
后记:
总之,当年震撼人心的concepts就没了。目前来看C++确实越来越臃肿而缓慢(这个不是说速度,而是指标准),不知道C++++(C#不算!)啥时能出现。或许虚拟机平台+部分编译才是真的出路?LLVM?