正统 C++:避开现代 C++ 弊端,代码易维护还兼容旧编译器!
正统 C++:避开现代 C++“坑”,还能兼容旧编译器!
2016 年 1 月 16 日,本文最初以要点形式发布 在此。下面详细了解一下正统 C++。
什么是正统 C++?
正统 C++(有时也被称为C+)是 C++ 的一个最小子集,它对 C 语言进行了改进,但避免了所谓现代 C++ 中所有不必要的东西。这与 现代 C++ 的理念正好相反。
为什么不用现代 C++?
早在 20 世纪 90 年代末,我们也是当时追求潮流的 C++ 玩家,使用着最新的特性,还劝别人也用。但随着时间推移,我们发现,仅仅因为某些语言特性存在就去使用它们是不必要的,有些我们用过的特性后来被证明并不好(比如 RTTI、异常处理和流),或者会带来不必要的代码复杂性。如果你觉得这是无稽之谈,那就再等几年,你也会讨厌现代 C++ 的(《为什么我不再花时间在现代 C++ 上》,存档的领英文章)。
为什么使用正统 C++?
“在 C++ 内部,有一个更小、更简洁的语言在努力破茧而出。”——比雅尼·斯特劳斯特鲁普
用正统 C++ 规范编写的代码库更易于理解和维护,也能在旧编译器上编译。用正统 C++ 子集编写的项目更容易被其他 C++ 项目接受,因为正统 C++ 使用的子集不太可能违背采用者对 C++ 子集的偏好。
正统 C++ 的 “Hello World”
#include <stdio.h> int main() { printf("hello, world\n"); return 0; }我应该用什么?
- 如果代码不需要更多的复杂性,那么类 C 的 C++ 是个不错的起点,不要添加不必要的 C++ 复杂性。一般来说,代码应该让熟悉 C 语言的人都能读懂。
- 不要做 这种事,在正统 C++ 中,“设计原理” 部分在 “非常简单,而且可用。结束” 之后就应该立即结束。
- 不要使用 异常处理。
- 不要使用 RTTI。
- 不要使用 C 运行时头文件的 C++ 包装(如 <cstdio>、<cmath> 等),而应使用 C 运行时头文件(如 <stdio.h>、<math.h> 等)。
- 不要使用流(如 <iostream>、<stringstream> 等),而应使用 printf 风格的函数。
- 除非你不关心内存管理,否则不要使用 STL 中任何会分配内存的部分。更多信息可参考 CppCon 2015:安德烈·亚历山德雷斯库《std::allocator 之于内存分配,就像 std::vector 之于烦恼》 的演讲,以及 为什么许多 AAA 游戏开发工作室不使用 STL 的讨论。
- 不要过度使用元编程来进行学术性的炫技。适度使用,仅在必要且能降低代码复杂性的地方使用。
- 对当前 C++ 标准中引入的任何特性保持谨慎,理想情况下,等待下一次标准迭代对这些特性进行改进。例如,C++11 中的 constexpr 在 C++14 中才变得可用(据 Jason Turner 所说,cppbestpractices.com 的策展人)。
- 不要使用 模块。
使用模块会带来以下缺点:
- 需要重写(可能还需要重构)你的代码。
- 丧失可移植性。
- 模块二进制文件(MSVC 除外)不可移植,所以无论如何都需要为库提供头文件。
- 项目构建设置变得更复杂。
- 除了最新版本的工具链,其他版本都无法工作(撰写本文时,苹果的模块支持被列为 “部分支持”)。
作为交换,你(普通开发者)能得到以下好处:
- 没有任何好处。
现在使用现代 C++ 的任何特性安全吗?
由于编译器、操作系统发行版等对 C++ 标准的采用存在滞后,通常无法立即开始使用新的有用语言特性。一般准则是:如果当前年份是 C++ 标准年份 +5,那么可以有选择地开始使用该 C++ 标准年份的特性。例如,如果标准是 C++11,且当前年份 >= 2016,那么可能是安全的。如果编译代码所需的标准是 C++17,而当前年份是 2016,显然你是在实践 “简历驱动开发” 方法。如果你是在为开源项目这样做,那么你创建的东西可能别人无法使用。
修订历史
更新截至 2025 年 1 月 14 日,正统 C++ 委员会批准有选择地使用 C++20。
- 2025 年 10 月 19 日 - 添加了关于模块的信息。
- 2019 年 1 月 16 日 - 添加了关于异常处理的信息。
- 2018 年 2 月 1 日 - 添加了关于 constexpr 需要多次迭代才能变得有用的信息。
- 2016 年 1 月 16 日 - 原文发布。
还有其他类似的理念吗?
- 嵌入式 C++ <https://en.wikipedia.org/wiki/Embedded_C%2B%2B>
- 名义 C++ http://archive.md/2016.08.07-162105/https://namandixit.github.io/blog/nominal-c++/
- 理智 C++ http://archive.md/2016.08.07-162220/http://flohofwoe.blogspot.nl/2013/06/sane-c.html
- 为什么你的 C++ 应该简单 http://archive.md/2017.03.19-055108/https://hacksoflife.blogspot.nl/2017/03/why-your-c-should-be-simple.html
- C++,不是你的问题,是我的问题 https://web.archive.org/web/20190227061553/https://c0de517e.blogspot.com/2019/02/c-its-not-you-its-me.html
- “保持 C 风格的简单” 亚历山大·拉德琴科悉尼 C++ 聚会 https://www.youtube.com/watch?v=lTXHOOwfTAo
- C++ 的一种方言 https://web.archive.org/web/20200521234043/https://satish.net.in/20180302/
- Defold 引擎代码风格 https://web.archive.org/web/20241003193318/https://defold.com/2020/05/31/The-Defold-engine-code-style/
- 正统性 - Clang 编译器的插件,可选择性禁用 C++ 语言的特定特性 https://github.com/d-musique/orthodoxy?tab=readme-ov-file#orthodoxy
代码示例
- 任何能在 C++ 编译器上编译的 C 源文件。
- DOOM 3 BFG <https://github.com/id-Software/DOOM-3-BFG>
- Qt <https://github.com/qtproject>(在不使用 RTTI 和异常处理的情况下构建)
- dear imgui <https://github.com/ocornut/imgui>
- bgfx <https://github.com/bkaradzic/bgfx>
- TheForge <https://github.com/ConfettiFX/The-Forge>
- Oryol <https://github.com/floooh/oryol>
- Network Next SDK <https://github.com/networknext/sdk>
—— 还有一件事...
我开发 bgfx 的使命是为游戏开发者提供一个跨平台、与图形 API 无关的渲染库,简化游戏在不同平台上的移植过程,确保无缝的性能和兼容性,而不会被特定引擎所束缚。如果你喜欢这篇文章并支持我的使命,请考虑成为 赞助者!❤️
