C++26:単純化された無限ループも未定義動作ではなくなります

2026/09/18 5:52

C++26:単純化された無限ループも未定義動作ではなくなります

RSS: https://news.ycombinator.com/rss

要約

日本語翻訳:

まとめ

C++26 は、P2809R3 を導入し、

while (true);
のような単純な無限ループがコンパイラによって沈黙的に削除されるのではなく、予測可能に動作するようにします。以前はこれらのループは未定義の振る舞いとして扱われており、Clang などのコンパイラによって完全に最適化・削除されてしまうことがしばしばあり、これにより予期せぬプログラムの実行や、ソフトウェアが破損した状態で停止せず継続して動作するというセキュリティ上の問題を引き起こすものでした。この問題を解決するために、新しい規格ではループ本体を
std::this_thread::yield()
の呼び出しに再定義し、C++ による前方進歩保証に必要なようにスレッドがその作業を続行することを確保しています。bare-metal システム上で動作する組み込み開発者は、ハードウェアが yield の欠如に依存している場合、わずかな変化を察知する可能性があります。しかしながら、この変更によりコードは正しく停止するか、プラットフォーム間で一貫した動作を示すようになります。セキュリティエンジニアは、これらのループが消滅することに基づく脆弱性を有するものは、コンプライアンスのあるビルドでは現在解決済みであることを留意してください。開発者は、完全な規格化に達する前に、より古い C++ モードにも既にこの論理を適用することが可能です。

元本文:

The original summary is already clear and comprehensive; therefore, I repeat it as-is:

Summary:

C++26 introduces Proposal P2809R3 to make simple infinite loops, such as

while (true);
, behave predictably instead of being silently removed by the compiler. Previously, these loops were considered undefined behavior, often causing compilers like Clang to optimize them away entirely. This led to unexpected program execution or security issues where software continued running in corrupted states rather than halting. To fix this, the new standard redefines the loop body to call
std::this_thread::yield()
, ensuring the thread continues its work as required by C++'s forward progress guarantee. While embedded developers on bare-metal systems might see slight changes if their hardware relies on missing yields, the shift ensures code halts correctly or behaves consistently across platforms. Security engineers should note that vulnerabilities relying on these loops disappearing are now resolved in compliant builds. Developers can already apply this logic to older C++ modes before full standardization is reached.

本文

C++26 でついに修正された「単なる無限ループ」の未定義挙動について

問題の概要:
while (true);
は危険だった?

プログラム例を検討してみましょう。

int main() {
    while (true)
        ;
}

もしこれが正しいと考えるなら間違いです。C++26 以前では、副作用がない

while (true);
ループは**未定義の振る舞い(UB: Undefined Behaviour)**として扱われていました。

  • コンパイラはこの無限ループが終了すると仮定できます。
  • Clangなどのコンパイラでは、この無限ループを完全に削除して最適化します。
    • 結果として、本来なら到達不可能なコードも実行される可能性があります。
    • これはバグではなく、UB という特性によるものです。

驚きの例:Clang での挙動

以下は Clang コンパイラ(C++23 モード)での実装例です。

#include <iostream>

int main() {
    while (true)
        ;
}

void unreachable() {
    std::cout << "Hello world!" << std::endl;
}
  • 実行結果: コンソールには「Hello world!」と表示されます。
  • 理由: 最適化により
    main
    内のループが削除されたため、本来なら到達不可能な
    unreachable()
    関数まで実行フローが伸びてしまいました。

問題の背景:なぜここまで来たのか?

この物語は C++11 に導入された**「進捗保証(Forward Progress Guarantee)」**から始まります。標準規格([intro.progress])では、スレッドが最終的には以下のいずれかを行うことを実装側に認めています。

  • スレッドを終了する
  • ライブラリの I/O 関数を呼び出す
  • volatile
    グローバル値へのアクセスを行う
  • シンクロニゼーションまたはアトミック操作を実行

問題点:

while (true);
はこれら何一つ実行しません。

  • C++26 以前: 進捗保証の規則により、このループで永久に留まることは「未定義の振る舞い」とされました。最適化器はこれを「永遠に立ち止まらない」と仮定し、ループを削除(到達不可能なコードとしてマーク)してしまいました。
  • C 言語との違い: C11 は
    while (1);
    を正しく定義されたプログラムとして扱いましたが、C++ はその追加の規則を採用せず、結果として不必要な分歧(Divergence)が生じていました。

なぜ最初から
while (true);
を書く人がいるのか?

実は、これは組み込みシステムやカーネルコードで**「エラー時の停止パターン」**として広く使われていたのです。

致命的なエラーが発生し、OS への復帰も不可能な場合、処理を止めるために以下のようなコードを書きます。

if (hardware_init_failed()) {
    log_error("fatal: hardware init failed");
    while (true)
        ;  // ハルト —— もはややるべきことはない
}
  • 問題: これまで C++ では UB とみなされていたため、最適化器がループを削除し、フローを次の関数(例:
    unreachable()
    )へ進ませることがありました。
  • 危険性: 組み込みシステムにおいて、これは致命的なエラーハンドラが意図せずデバイス停止を行わず、破損したハードウェアに対して偶然の命令を実行させることにつながります。セキュリティ上重大な脆弱性となります。

C++26 における変更点:P2809R3 の承認

C++26 でこの問題は P2809R3 によって解決されました。単なる無限ループはもはや UB ではなく、明確に正しく定義されたものとなりました。また、欠陥報告(Defect Report)として承認されたため、古い C++ モードでも実装側が同様の修正を適用する可能性があります。

「単なる無限ループ(Trivial Infinite Loop)」の定義

P2809R3 は意図的に狭いカテゴリーを定義しました。以下の 2 つの条件を両方満たす場合、ループ本体は

std::this_thread::yield()
の呼び出しに置き換えられ、進捗保証が適用されます。

1. ループ本体

  • 自明な空の反復ステートメントであること
    • 文字通り空(
      ;
      または
      {}
      )である必要があります。
    • 意味のない表現式を含む場合(例:
      "a string";
      )、条件を満たしません。

2. 制御式

  • true
    を評価する定数式であること
    • for
      ループで条件がない場合は
      true
      が暗黙的です。

該当・不該当の例

コード単なる無限ループ?理由
while (true);
✅ はい完全な空ループ + 定数式
for (;;);
✅ はい空ループ + 暗黙の true
do {} while (true);
✅ はい空ループ + 定数式
constexpr bool go = true; while(go);
✅ はい —
go
は定数式
制御式が定数
while (true) { "a string"; }
❌ いいえ本体に意味のあるステートメントあり
while (true) if (done) break;
❌ いいえ本体は空ではない(制御フローを含む)
while (true) if constexpr (false) break;
❌ いいえ構文上自明な空の反復ステートメントに一致しない
bool done = false; while (!done);
❌ いいえ制御式が定数式でない

変更の結果:

  • 最適化器は「単なる無限ループ」を UB と扱うことができなくなりました。
  • 実行は必ずその先へ続くと仮定する必要があるため、ループの削除最適化を行うことはできなくなります。

非標準環境(Freestanding)への注釈

非標準環境の実装(例えば裸金属環境など)では、

std::this_thread::yield()
への置き換えは実装に依存します。

  • 意図的な停止ループを協調的なイールドに変えてしまうと、プログラマーの意図とは異なる挙動を引き起こす可能性があります。

おわりに

  • while (true);
    が UB であったことは、C++ の特徴として知られており、C 言語からの不必要な分歧(Divergence)でした。
  • これにより実際の組み込みコードが破壊されたり、コンパイラが安全性を誤って保証したりするリスクがありました。
  • C++26 ではこれらが修正されました。単なる無限ループは正しく定義され、コンパイラによる除去最適化はもはや行われません。

同じ日のほかのニュース

一覧に戻る →

2026/09/19 6:00

これまでに Claude.md が存在しない場合、Claude Code は現在 AGENTS.md を読み取るようになりました。

2026/09/19 3:51

さらに 100TB のメモリーを節約

## Japanese Translation: Cloudflare は、トラフィックの分散に常時ハッシュリング(consistent hashing)を処理する Pingora バックエンドルーターの一部である `pingora-ketama` コンポーネントの最適化により、メモリ使用量を成功裡に削減しました。変更前に、システムはサーバーごとに過剰なハッシュエントリを格納しており、コンプライアンスとキャッシュの必要性により数十個の別々のハッシュリングが生じる場合があり、一部のケースでは 6GB に達することもありました。統計解析により、サーバーあたりに単一のハッシュのみを使用すると深刻な不均衡(変動係数約 99%)が発生し、業界標準デフォルトはハッシュ数を約 160 としていることが示されました。数学的な導出により、32 ビット値に対して 10,000~100,000 ハッシュを超えると追加容量が限界に達し衝突リスクが増大することが確認されました。エンジニアは、サーバーごとの生成されるハッシュ数を 90% 削減しても分布誤差が大きくならないことが安全に確認できました。構造レベルでは、完全な構体(struct)全体を 8 バイトのインデックス(`u32`)と、4 バイトのハッシュを圧縮された生バイト配列形式に置き換えることで、エントリあたりのメモリ使用量を 25% 削減しました。新コードは、非公開の機能フラグを通じて段階的に導入され、旧バージョン(大リング)と新バージョン(小リング)が共存可能となっています。ロールアウトは小規模な検証ロケーションから始まり、グローバルなキャッシュ churn を回避し安全な移行を確保するよう層状に行われました。これらの変更により、サーバーあたりのハッシュ生成数を 90% 削減し、グローバルメモリ消費量を 100TB 以上削減することで、コスト効率、信頼性、ロールバックの安全性を向上させました。

2026/09/18 23:18

クラウドフレイク・クイックトンネル

## Japanese Translation: 本テキストは、アカウント、DNS 設定、または開放ポートを必要とせず、開発環境向けに安全なパブリック URL を瞬時に生成する強力なコマンドラインツールを紹介しています。Cloudflare のグローバルインフラストラクチャを活用することで、このソリューションは 335 都市以上に対応し、構築済みの TLS と DDoS 保護を備えた即時のアウトバウンド専用暗号化接続を提供します。このアプローチは、`npm run dev` などのツールのエンドポイントを一貫して共有しながら既存のコードベースを変更しないようにすることで、開発者のワークフローを簡素化します。 処理は約 3 秒で完了し、構造化された JSON(ホスト名、エッジロケーション、ヘルスステータスを含む)として URL をコンソールに直接印刷して簡単なパースを可能にします。重要なのは、これらのトンネルは一時的で、ホスティングプロセスが停止すると自動的に終了し、手動での片付けを必要としないことです。この設計により、シンプルな JSON ホスト名を用いて、Webhook(例:Stripe、GitHub)、コーディングエージェント、および人間によるブラウザからローカルサービスへとの統合を容易にします。最終的に、これは内部マシンを公開する際の課題を解決し、Anycast ルーティングを介して最近のエッジノードへと接続することで不要なオーバーヘッドなしに、プライベートの localhost アプリケーションとパブリックインターネットの間で効率的な橋渡しを提供します。

C++26:単純化された無限ループも未定義動作ではなくなります | そっか~ニュース