C++26:无限循环不再是未定义行为

C++26: Trivial infinite loops are no longer undefined behaviour

在 C++26 之前,一个看似无害的 while (true); 循环竟然属于未定义行为。编译器如 Clang 曾利用这一规则将其优化掉,导致程序意外执行后续代码,这在嵌入式系统中可能引发严重的安全漏洞。C++26 通过提案 P2809R3 修正了这一缺陷,将“平凡无限循环”定义为明确行为,并引入 std::this_thread::yield() 机制。这一改变不仅消除了与 C 语言的不必要差异,也修复了真实场景中的代码隐患,让开发者不再担心编译器“自作聪明”地移除关键逻辑。

当优化器移除循环时,执行流程会落入链接器放置的后续代码中,就像本文开头的‘Hello world!’示例所展示的那样。
  1. JoshTriplett

    > 当两个条件都满足时,循环体将被替换为对 std::this_thread::yield() 的调用。

    在此处插入尖叫。

    一个没有任何库调用的无限循环,竟然被插入了一个系统调用。这简直是等待发生的可怕惊喜。

    “前向进度保证”(forward progress guarantee)的整个概念都崩塌了。无限循环应该编译成无限循环。仅此而已,不多不少。

  2. wahern

    > 当两个条件都满足时,循环体将被替换为对 std::this_thread::yield() 的调用。这赋予了循环此前缺乏的前向进度语义。

    这正是 Linus 和许多其他人所厌恶的 C++ 中“隐藏代码”弊端的极致体现。对于构造函数和析构函数来说,这在某种程度上是不可避免的,也不那么随机,尽管 Rust 在限制非本地代码的爆炸半径方面做得更好,至少在 drop 的情况下是这样。

    如果他们不想采纳 C11 的规则,C++ 委员会本应探索一种规则,要求编译器对琐碎循环(无论是按 C11 定义还是其他方式)发出诊断信息或错误,强制程序员显式插入 ::yield 或类似操作。没有隐藏代码,编译器做出令人惊讶之事的余地也更小。

    C 委员会一直在标准中严格列举未定义行为(UB)的情况,并逐一处理,通常是通过要求诊断信息、报错或将其转变为实现定义的行为。但像这样插入代码是难以想象的。

  3. Aurornis

    我从未想到 unreachable() 函数会在那个示例中被执行。这大概不是你在实践中会遇到的情况,尽管我也见过一些由多层 #ifdef 引发的奇怪现象。

  4. omoikane

    > 循环必须是一个平凡的空迭代语句——这意味着其主体字面上是空的

    这似乎意味着循环体不能是 "continue"。确实,我刚刚尝试了 -std=c++26,使用 ";" 时正如承诺的那样得到了无限循环,但 "continue" 却恢复了未定义行为:

    - "while(true);" -> https://godbolt.org/z/T65o51crx

    - "while(true) continue;" -> https://godbolt.org/z/Pj9raEcnP

    这很不幸,因为我知道有一份风格指南更倾向于使用 "continue" 而不是单个分号。我想从此以后,所有这些代码都将变成 "while(true) {}" 了。

    https://google.github.io/styleguide/cppguide.html#Formatting...

  5. ameliaquining

    这篇文章很不幸地没有解释为什么有人一开始就希望无限循环是未定义行为(UB)。我找到了这个解释:https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1528.htm

同日更多故事

2026-09-18