序
“C++是一门复杂的语言。”这是C++在数十年的应用中所获得的评价,但该评价也并非总是正确的。通常,这是人们用来劝退他人学习C++的理由,也是人们认为另一门编程语言更好的原因。这些论点很难得到论证,因为基本前提是错误的,也就是说C++并不是一门复杂的语言。C++最大的问题是人们对它的评价,第二大问题是没有针对它的高质量教材。
该语言从C语言发展而来。它最初只是C语言的一个分支(只增加了一些小功能),对应的编译器是一个叫作Cfront的预编译器。Cfront将早期的C++代码编译成C代码,然后由C编译器进行处理,这便是其名称的由来。经过几年的开发之后,人们发现这种方式限制了语言的发展,于是开始着手创建真正的编译器。这个编译器由Bjarne Stroustrup(该语言的最初发明者)编写,可以独立编译C++程序。其他公司也对扩展基本的C语言感兴趣,各自开发了自己的C++编译器,这些编译器大多与Cfront或较新的编译器兼容。
后来这被证明是难以持续的,因为这门语言难以跨平台移植,而且不同的编译器之间严重不兼容,更不用说所有的决定和方向都控制在一个人手里。制定一个跨越具体公司的国际标准——一门有标准过程的语言,并且让某些组织去管理它显然更为合理。因此,C++开始成为ISO标准。经过若干年的发展,第一个正式的C++标准于1998年问世,人们对此欢欣鼓舞。
但人们只高兴了一阵子,因为虽然C++98是一个很好的标准,但它包括了一些人们没有预料到的新功能,而且有一些功能的交互方式很奇怪。在某些情况下,这些功能本身很好,却无法与其他功能交互——例如将std::string作为文件名打开文件时。
另一个较晚增加的功能是模板,这是支持标准模板库的底层技术,标准模板库是当今C++非常重要的部分之一。在它发布之后,人们才发现它本身居然是图灵完备的,许多高级结构可以通过编译时计算获得。这大大增强了库作者编写能够处理任意复杂的推理的泛型代码的能力,这与当时的其他语言非常不同。
最后一个编译问题是,虽然C++98很好,但许多编译器难以实现模板。当时的两个主要编译器,即GCC 2.7和Microsoft Visual C++ 6.0,都不能进行模板所需的两阶段名称查找。要完全解决这个问题,唯一的办法是对编译器进行全面重写……
GNU试图继续在现有的代码库中增加新功能,但最终在GCC 2.95时打算重写。这意味着在多年的时间里不会有新的功能或版本,许多人对此感到不满。一些公司利用这个代码库并试图继续开发,创建了2.95.2、2.95.3和2.96版本——这三个版本都因为缺乏稳定性而被大家记住。最后,完成重写的GCC 3.0终于问世。它最初并不是很成功,因为尽管它能比2.95版本更好地编译模板和C++代码,但不能将Linux内核编译成可运行的二进制文件。Linux社区明确反对修改其代码以适应新的编译器,坚持认为是该编译器的问题。最终,在3.2版本的时候,Linux社区才开始改变,重新围绕GCC 3.2及以后的版本展开开发工作。
微软也试图尽可能地避免重写编译器。它增加了一个又一个特殊补丁,并采用启发式方法来猜测某些东西是否应该在第一阶段或第二阶段的模板名称查找中解决。这几乎是有效的,但21世纪10年代初期编写的库程序显示,已经没有办法使所有的程序都工作了——即使修改源代码也不行。微软最终重写了解析器,并在2018年发布了更新版本——但许多人没有启用新的解析器。到了2019年,新的解析器才终于被默认在新项目中启用。
但在2011年有一个重大事件:C++11发布了。在C++98发布之后,人们陆续提出了很多重要的新功能并持续推动其发展。但是,由于一个特别的功能没有达到预期的效果,新C++版本的发布从2006年左右被推迟到2009年左右。在那段时间里,人们试图让它像新功能一样工作。2009年,它最终被去掉了,其余功能也被重新修复一番,这样1998年版的C++终于得到了更新。新版本包含大量的新功能,并增强了库功能。编译器再次缓慢地跟上,大多数编译器直到2013年年底才可以编译大部分的C++11代码。
C++委员会从先前的失败中吸取了教训,制定了一个每三年发布一个新版本的计划。该计划是在第一年创造和测试新的功能,在第二年将其很好地整合,在第三年使其稳定下来并正式发布,每三年重复这一过程。C++11是第一个成果,而2014年版C++是第二个成果。值得称赞的是,委员会完全按照承诺做了,在C++11的基础上进行了重大更新,并使C++11的功能比以前更好用。虽然在很多地方包含了一些限制,但是这些有限制的地方后来也逐步完善到大家能接受的程度——特别是constexpr相关的功能。
仍在努力追赶所有C++11新功能的编译器作者们终于意识到,他们需要调整自己的步伐,否则就会被甩在后面。到2015年,所有的编译器支持几乎所有C++14的功能——鉴于之前C++98和C++11的情况,这是一个了不起的成就。所有主要的编译器作者也重新加入了C++委员会——如果编译器作者在某个功能发布之前就知道它,那么相应编译器就可以成为支持该功能的主要编译器。如果编译器作者发现某个功能与自己的编译器设计不匹配,那么可以影响C++委员会对功能进行调整,使其更容易得到支持,从而使人们更早地使用它。
现在,C++正在经历一个重生期。这一时期大约始于2011年,当时C++11被引入,它所倡导的“现代C++”编程风格逐渐被大家采用。但是直到最近这些改进才更为显著,因为C++11的所有理念都在C++14和C++17中得到了微调,而且现在所有的编译器都完全支持人们所期望的所有功能。更棒的是,C++20标准已发布,所有编译器的最新版本已经支持该标准的大部分功能。
现代C++允许开发者不用走之前的学习老路了,即不用先学习C,然后学习C++98,再学习C++11,最后摒弃C和C++98中所有已得到改进的部分。大多数课程过去都从C++历史开始介绍,因为有必要了解为什么有些东西会这么奇怪。不过对于本书,我在这里加入了这些信息,因为Josh完全忽略了这一点。
因此,读者在学习C++时不需要再了解这些历史了。现代C++风格允许读者完全跳过它,只需知道C++的基本原则就可以写出设计良好的程序。现在就是学习C++最好的时机。
现在我们回到先前的问题——没有针对C++的高质量教材。如今,C++委员会内部也提供了高质量的C++教学——有一个研究小组专门负责C++的教学!在我看来,这个问题已经被这本书完全解决了。
与我读过的所有其他C++书籍不同,这本书介绍基础知识和原理。它教读者如何分析,然后让读者通过标准模板库提供的东西进行分析。获得回报可能需要更长的时间,但当你完全理解C++的工作原理,看到第一个结果被编译和运行时,你会感到非常满足。本书甚至包含了大多数C++书籍都回避的话题:在运行完整的程序之前设置环境并测试代码。
享受阅读本书之旅并尝试所有的练习吧!祝你在C++的道路上好运!
Peter Bindels
To m To m首席软件工程师