C++,这门承载了数十年辉煌历史的编程语言,曾是系统软件、游戏开发和数据库系统等关键应用的基石。然而,伴随着对更高安全性和效率的需求,C++的未来正面临愈发明显的分裂。近日,软件工程师Mond发布的《TheTwoFactionsofC++》一文引发了热烈讨论,他详细剖析了这门语言当前的困境:一方面是现代技术企业对更高性能和工具链的渴望,另一方面则是庞大的遗留代码库对向后兼容性的刚性要求。与此同时,C++标准委员会试图在不破坏现有代码的前提下引入新特性,这种努力能否弥合分歧,仍是悬而未决的问题。
作者|Mond翻译|屠敏
出品|CSDN(ID:CSDNnews)
C++的现状
当前,我们所使用的C++似乎正处于以下局面:
1.C++的演进工作组(EWG)刚刚就采纳P3466R0达成共识——即,(重新)确认未来C++演进的设计原则:
这无论这是好是坏,都表明C++的发展方向在进一步巩固当前的轨迹。
2.与此同时,一方面,美国政府希望人们停止使用C++:
截至目前,美国政府的各个部门已经发布了文件、报告和建议,警告行业不要使用内存不安全的语言。
3.各种大型科技公司正在采用Rust:
说到大型科技公司,近期还有几件事值得注意:
4.社区中的问题:
我不知道你怎么看,但如果我作为一个外行人来看待这一切,C++似乎基本上已经分崩离析了,而且似乎很多人已经对C++委员会能够处理这些问题的能力失去了信心。
C++的两大派系
人们似乎正在寻找其他解决方案。
比如说Google。自从ABI投票以来,Google显然对“这一标准化流程”失去了信心。这种失望并非针对C++语言本身——Google自己拥有庞大的C++代码库,这门语言对他们来说一直表现出色。然而,他们对C++在多方压力(如潜在的政府监管、其他语言的竞争、大厂对更高性能和安全保障的需求等)下继续演进的能力失去了信心。
那么问题出现在哪里?为什么C++不能做出改变呢?
答案其实很简单。我们可以参考HerbSutter在其关于配置文件的论文中所说的话:
“我们必须尽量减少对现有代码的修改需求。根据几十年的经验,对于拥有大型代码库的大多数客户来说,即便是为了安全原因,也不会因为严格性规则而修改哪怕1%的代码,除非有法规要求强制执行。”
——HerbSutter
这很合理,不是吗?没人对此感到惊讶。
现在,对比一下Google工程师ChandlerCarruth在WG21成员页面上的简介:
“我主导了基于Clang的C++工具和自动化重构系统的设计,这些工具现在已成为Clang项目的一部分……
在Google内部,我带领团队将这些基于Clang的自动化重构工具扩展到整个代码库,超过1亿行C++代码。我们可以在20分钟内对整个代码库进行分析并应用重构。”
看到了吗?这里ChandlerCarruth提到了一个关键词是“自动化工具”。但不只是自动化迁移工具,这只是最显眼的例子。
两种C++用户阵营的对立
实际上,我们看到的是两种完全不同的C++用户阵营之间的冲突:
这两种用户之间的关键区别在于:前者能够相对顺利地应对迁移,因为他们能从版本化源码构建整个C++栈,而后者则仍在使用1998年的老旧库。
这种能力——从版本化源码构建整个依赖栈(最好还带有自动化测试)——是两大阵营之间最重要的分水岭。
当然,在实践中,这是一个渐进的过程。我可以想象,要把大公司的代码库从可怕的泥球变成半可管理、可构建、经过代码检查、适当版本化、稍微不那么可怕的泥球,必须流下多少汗水、泪水、账单和心血。
事后看来,很容易认为这一切都是不可避免的:像Google这样的公司(使用相对现代的C++,拥有自动化工具和测试,以及现代基础设施)的需求与强烈向后兼容的愿望之间存在明显的脱节。
大胆地说,单一、无方言且统一的C++的概念似乎已经死多年了。至少,我们有两种主流风格的C++:
你会注意到,这两种文化的分歧不在于C++语言本身,而是工具和是否能从版本化源码进行干净、有定义的构建。理想情况下,甚至能够在不需要记住以前开发者通常设置的那个标志或环境变量的情况下进行部署。
例如,Google的代码库是否完全采用“现代”C++习惯用法并不是重点。关键是工具是否到位,以及是否能够从源码构建。
很多人会说工具不是C++标准委员会的责任,这观点是对的。工具确实不是C++标准委员会的责任,因为C++标准委员会放弃了对它的责任(他们专注于语言规范,而非具体的实现)。这是设计使然,考虑到遗留负担很难去责怪他们。C++是一个统一不同实现的标准。
话虽如此,如果说Go语言有一件事做对了,那就是他们把工具放得很重要。相比之下,C++出生于一个比linter更古老的时代。C++没有统一的构建系统,也没有接近统一的包管理系统,语法复杂难以解析(这对工具来说很糟糕),且每次改动都在与Hyrum定律作斗争。
这两个派系(良好的工具,可以毫不费力地从源码构建vs.差劲的工具,无法从源码构建)之间存在着巨大的、不断扩大的裂痕,而且短期内看不到弥合的可能性。
C++委员会似乎坚决维护向后兼容性,无论代价如何。
顺便说一句,我并不一定反对这一点!向后兼容性对许多人来说非常重要,理由充分。然而,对另一些人而言,这并不重要。这不是谁“对”的问题,而是两种截然不同、无法调和的立场之间的冲突。
造成的后果
这就是为什么配置文件的设计是这样的:安全配置文件的目的并不是为了解决现代、技术娴熟的C++公司的需求。它们是为了在不需要对旧代码进行任何更改的情况下带来改进。
同样地,对于模块也是如此。设计初衷是让你“只需”以模块形式导入头文件,而不会引发任何向后兼容性问题。
毫无疑问,大家都喜欢那些可以直接引入并在不改动旧代码的情况下带来改进的功能。但很明显,这些功能的设计初衷是为了迎合“传统C++”的需求。任何需要从传统C++迁移的功能对C++标准委员会来说都是不可行的,因为正如HerbSutter所说,你基本上不能指望人们自己去迁移。
(再次强调,考虑传统C++的需求并不是坏事。这完全是一个合理的决策。)
这一点我始终牢记在心,每当我阅读C++提案时都会注意:C++其实有两个主要受众群体。一是现代C++用户,另一个是传统C++用户。这两个阵营对许多问题的看法大相径庭,且许多提案都是针对其中一个特定群体的需求而撰写的。
显然,这导致了许多人无法互相理解,话说不到一块:尽管许多人以为安全配置文件和safeC++是在解决同样的问题,实际上它们面向的是完全不同的受众,解决的是完全不同的问题。
C++委员会正试图阻止这种裂痕进一步扩大。这或许就是为什么SeanBaxter撰写的《SafeC++》对他们来说毫无意义的原因。这种提案是一种激进且全面的变革,可能会开创一种完全不同的C++编程方式。
当然,也有人提出另一个问题:是否某些C++标准委员会成员只是过于固执,抓住各种理由来阻止他们个人在审美上不认可的演进。
这是否属实,我不便评判,但关于C++标准委员会应用“双重标准”的说法并非首次听闻。比如“如果你想让这个提案被通过,我们要求你提供多个编译器的完整、可用的实现;但我们仍然愿意支持某些大项目(例如模块、配置文件),即便这些项目根本没有任何可用的概念验证实现。”
如果真是如此(我无法确证),我无法预测C++还能沿着这条道路走多久,除非最终发生更为剧烈的分裂。
更不用提,打破ABI兼容性可能引发的巨大麻烦和一系列问题,那简直就是个无底洞。
C++未来将何去何从?
面对现代企业和遗留系统给C++社区内部带来两极分化的趋势,不少网友看法不一。
来自Reddit的用户ravixp表示:
(想象一下试图对这样的代码添加静态分析支持:它同时包含了std::string、C风格字符串,以及20年前STL性能较差时团队自行创建的字符串类型的奇怪中间状态!)
关键在于,现代化的成本非常高昂。这里提到的“现代”C++不仅仅是用不同的方式编写代码,它还包括一整套支持现代化标准的工具体系,这可能需要从零开始构建,还需要一个能够跟上C++演进的工程团队。
需要牢记的是,这里的冲突并不是“喜欢遗留C++”的人与“喜欢现代C++”的人之间的矛盾,而是“能负担得起现代C++”的人与“负担不起”的人之间的矛盾。C++的确需要改变,但真正的问题是:我们集体能够承受多少变化的成本,以及如何从中获得最大的价值。
Kronikarz评价道:
从道德角度来看,我认为这反映了一种分歧:一部分人对C++逐渐变成下一个COBOL不以为意,另一部分人则对这一想法感到排斥。
另一位网友KittensInc则认为:
如果像缓冲区溢出这样的漏洞被认为是完全可以预防的,那么从逻辑上讲,如果某次黑客攻击、勒索软件事件或数据泄露的根本原因是缓冲区溢出,保险公司可能会拒绝赔付。这样一来,公司可能会要求软件供应商对其代码库进行第三方静态分析审计。
于是我们到达了这样一个节点:不进行现代化改造的成本变得过高。你要么升级你的代码库,要么你的公司走向灭亡。采用现代开发实践的企业只需运行一些简单的分析工具并完成一些文书工作,而那些没有任何像样工具并且背负着数十年技术债务的公司将陷入严重困境。
对此,你怎么看?
目前距离2024全球C++及系统软件技术大会召开仅剩6天,现在报名可享九折优惠,抓住最后1天优惠期,立即锁定席位!