C 与 C++ 40 年的爱恨情仇( 二 )


那些存在于C++标准库中但主要声明来自C的函数,很难声明成constexpr,更难声明成noexcept 。C的兼容性会导致性能成本,而C函数是优化的障碍 。
许多C的结构在C++中都是有效的,但无法通过代码审查(如、longjmp、malloc、构造/析构函数、free、C风格的类型强制转换等) 。
在C看来,这些惯用写法可能问题不大,但在C++中可不行 。C++具有更强大的类型系统,不幸的是,C的惯用写法在这个类型系统中凿了一个洞,因此实现C的兼容性需要在安全性方面付出代价 。
别误会,C++仍然关心C的兼容性,某种程度上 。然而,有趣的是C也很关心C++,某种程度上 。实话实说,C对C++的关心程度可能高于C++对C的关心 。看来,每个委员会还是在乎另一个委员会的工作 。但我们很不情愿 。
C++知道,许多基础库都是用C编写的,不仅包括libc,而且还有zip、png、curl、openssl(!)以及许多其他库,无数的C++项目都在使用这些库 。C++不能破坏这些兼容性 。
但是最近,尤其是在过去的十年中,C++的规模已远远超过C 。C++拥有更多的用户,并且社区更加活跃 。也许这就是为什么如今C++委员会的规模是C委员会的10倍以上 。
C++是不可忽视的力量,因此C委员会必须考虑不破坏C++兼容性 。如果非要说一个标准追随另一个标准对话,那么如今C++是领头者,而C是追随者 。
现在,C++处于稳定的三年周期中,无论是风雨还是烈日,抑或是致命的新疫情 。而C每十年左右才发布一次主版本 。不过这也很合理,因为作为一种较低级的语言,C不需要发展得那么快 。
C语言的环境也与C++完全不同 。C多用于平台,更多地用于编译器 。每个人(甚至他们的狗狗)都会编写C编译器,因为该语言的特性集很小,所以任何人都可以编写C编译器 。而C++委员会真正考虑的实现只有四种,而且在每次会议上这四种实现都会出现 。所以,C语言中的许多功能都是与实现有关的,或者是可选支持的,这样各种编译器不需要做太多努力就可以声称自己遵从了标准,据说这样委员会的人会比较高兴 。
如今,C++更加侧重于可移植性,而不是实现的自由 。这又是一个理念的不同 。
 
因此,你的提议破坏了C的兼容性我提议的P2178的一部分理论上会影响与C的兼容性 。这样的话所有方案都不会令人满意 。
有人可能会说,你可以先向C委员会提议你的新特性 。这意味着需要召开更多会议 。C会议的严格出席规则可能导致你无法参加会议,这就将那些不愿意花上数千美元成为ISO会员的个人拒之门外 。这是因为C委员会必须遵守ISO的规则 。
而且,如果新的标准刚刚发布,那么可能还需要等待十年时间,你的提案才会被考虑 。最重要的是,如果C委员不理解或不在乎你正在努力解决的问题,那么你的提案就石沉大海了 。或者他们可能没有精力来处理这个问题 。而且,可能你也没有精力来处理C 。毕竟,你的本意是要改进C++ 。实际上,哪怕会议上无人反对你的提议(尽管不太可能发生),如果有人让你先去跟C委员会的人讨论,就等于给你的提议判了死刑 。
另一种可能的情况是,C委员会接受与C++中存在的版本略有不同的版本 。true只能做一个宏来实现 。char16_t需要通过typedef 。char32_t不一定是UTF-32 。static_assert对应的是 _Static_assert 。
这类的情况还有很多,我们应该责备C吗?可能不应该 。他们的委员会只是在尽力将C语言做好 。反之亦然 。在C++20中,指定的初始化器就受到了C的启发,但采取了略微不同的规则,因为如果完全一样的话就不符合C++的初始化规则 。
对于这个问题,我也有责任 。C有VLA 。如果当时我在,我一定会反对在标准C++中采用它,因为它导致了太多安全性问题 。我也会坚决反对将_Generic添加到C++中的提议 。也许_Generic的目的是减少由于缺乏模板或缺乏重载而导致的问题,但是C++有这两个功能,从我的角度来看,_Generic并不适合我想象中的C++ 。
这两个委员会似乎对于对方语言的关心程度也不一样 。有时我们会遇到兼容性非常好的情况(std::complex),有时完全不在乎兼容性(静态数组参数) 。
这没有办法 。别忘了每个委员会都是一群人,他们在不同的时间、不同的地点投票,而试图控制结果会导致投票毫无意义 。将这些人放在同一个房间也不现实 。ISO可能会反对,参与者的不平衡会导致C的人处于极大的劣势 。
 
C的兼容性不重要如果你是C开发人员,那么肯定会把C视为一种简洁的编程语言 。但对于我们其他人而言,C的印象完全不同 。


推荐阅读