CppCon 2026が終了し、発表資料が公開されました。YouTubeの方はそのうちアップロードされると思います。
主に自分用にClaude Codeにすべての発表資料を要約させたものを、ここで公開します。興味がもてそうな発表を見つけたらくわしく見る、という用途に使います。CppConのWebサイトに載っている発表概要を翻訳したものではなく、発表資料内の要点をまとめたものです。
Herb Sutterの基調講演だけまだ公開されていないようなので、あとで追記するかもです。
【1. C++26 と標準化】
Profiles for Simplicity and Guarantees — Bjarne Stroustrup
「安全性・優雅さ・効率のすべてが必要だ。優れたプログラマならモダン C++ で書ける。だが 1,840 万人の開発者全員が常に高品質を出せるわけではない」という問題意識から出発する。
Profile とは「静的解析と実行時チェックの組み合わせによって強制される、望ましい性質の一貫した保証の集合」—たとえば「すべてのオブジェクトは使用前に初期化される」「ダングリングポインタ経由のアクセスは存在しない」。違反時の挙動は profile が規定し、強制されることが保証される。意味論は変えない(従来 UB だったものに意味を与える点を除く)。
GCC・Clang の両方に実装が進んでおり(P3704、P3446 ダングリング除去、P4222 初期化 profile)、「Profiles は C++ の使われ方・教えられ方・見られ方を変える」というのが結論。
C++26: A Curated Tour of What's New — Marc Gregoire
2026 年 3 月に技術作業が完了した C++26 の全体ツアー。Herb Sutter 評は「C++11 以来もっとも魅力的なリリース」。
トップ 4 機能は静的リフレクション、Contracts、実行制御ライブラリ(std::execution)、そして UB の削減 —C++26 として再コンパイルするだけでコードはよりメモリ安全になる、という点が強調される。コア言語側は _ プレースホルダ、= delete("reason")、構造化束縛の拡張、パラメータパックへの添字アクセス、#embed、constexpr 例外など、ライブラリ側は std::inplace_vector、std::hive、飽和演算、<linalg>、views::concat、std::simd、std::function_ref など。
膨大な小改良(trivial relocatability、<rcu>、<hazard_pointer>、標準ライブラリの堅牢化(hardening)など)まで一望できるのが本発表の価値。
How C++26 Changes the Way We Write Code — Timur Doumler
Coroutines、Concepts、Ranges、Modules、リフレクション、Contracts、std::execution、std::simd に共通するのは、「意図をより直接的に表現するための抽象」であり、長年の設計変遷と論争を経ており、C++29 以降への拡張が前提になっている、という点。
そこで機能を覚えるのではなく理解するための問いを立てる —「この機能は何の抽象か」「抽象の境界はどこか」「現実世界のアナロジーは何か」。たとえば std::simd は「SIMD レジスタを可搬に操作する抽象」ではなく「データ並列性を可搬に表現する抽象」と捉え直される。
後半はリフレクションの実例(to_json、配列構造体から構造体配列への変換、test_ で始まる関数を全実行するテストランナー、アノテーションによるスキップ)で、C++26 が書き方そのものをどう変えるかを具体的に示す。
The Real Story of C++26: Beyond the Headline Features — Sandor Dargo
冒頭に置かれるのは const int& f() { return 42; } の 1 行で、今日は運がよければ警告付きで通るこのコードが、C++26 ではコンパイルを通らなくなる。リフレクションも Contracts も関係なく、ただバグが 1 つ存在しなくなる。「大きな機能は何ができるかを変え、小さな機能は何が実用的かを変える」という立場で 5 つのテーマを追う。
正しさの再定義では、ダングリング参照の返却(P2748R5)や不完全型の delete(P3144R2)が不適格(ill-formed)になり、未定義動作と規定された動作の中間としてエラー性動作(erroneous behavior)が導入される。未初期化値の読み出しは誤りのままだが、被害の範囲は限定される。ライブラリの堅牢化(hardening、P3471R4)はメモリ安全性に関わる事前条件だけに的を絞り、Google が数億行に適用した結果は 1,000 件超のバグ検出、セグメンテーション違反 30% 減、オーバーヘッド平均 0.30% だった。
ほかに constexpr な throw(実行時とコンパイル時で同じ関数を使える)、std::function_ref、std::copyable_function(const をシグネチャの一部として扱い、std::function の const バグを避ける)、パラメータパックへの添字アクセスなど。結論は「これは機能一覧ではなく方向である — 正しい C++ を書きやすく、正しくない C++ を捕まえやすくする」。
Towards a Complete Contract-Assertion Facility for C++ — Timur Doumler
契約を「プログラムが正しいとみなされる条件」、契約違反を「ユーザーのエラーではなくプログラマのバグ」と定義するところから始める。
C++26 の Contracts は「最小限の実用製品(MVP)」であり、基盤は堅牢だが大規模に使うには不足しているとして、C++29 に向けた 6 つの拡張(P3850)を解説する: 仮想関数への pre/post、ユーザー定義エラーメッセージ、明示的な契約違反の発火、事後条件のキャプチャ、表明制御オブジェクト(ラベル)、暗黙の契約表明。
最後の「暗黙の契約表明」が要で、配列の範囲外アクセスのような UB を pre(i < N) として概念的に定義できれば、ignore は通常のコンパイラ、enforce は AddressSanitizer、quick-enforce は -fbounds-safety に対応づく(P3100)。狙いは新技術の発明ではなく、既存のサニタイザやコンパイラフラグを標準の語彙と違反ハンドラの下に統合すること。
IEEE 754 Decimals for C++: The Boost.Decimal Library — Matt Borland
0.1 + 0.2 が 0.30000000000000004 になるのは精度ではなく表現可能性の問題である(5 は 2 を割り切らない)、だから「もっと精度を」は解にならない、という導入。
そこで金融・人間が入力したデータ・法規制対応のために IEEE 754 の 10 進浮動小数点を C++ に持ち込むのが Boost.Decimal。ヘッダオンリー・依存なし・C++14・全面 constexpr・CUDA 対応で、<cmath> <charconv> <format> まで完備し、x86_64/x86_32/ARM64/ARM32/s390x で検証されている。
実利用も挙げられる。Satoshi(1 億分の 1 BTC)を自前の固定小数点で表現できなかった取引会社、ARM で decimal128 が必要だったデータベース会社、Boost.MySQL の DECIMAL 型など。結論は「データが 10 進なら型も 10 進であるべき。演算にはコストがあるが、パースとシリアライズでは速くなりうる」。
The State of Boost: Boost Is Where You Become the Engineer You Want to Be — Harold Bott
C++ Alliance CEO による Boost の現況報告。3 幕構成(作り方 / 実績 / 今後)。
中心にある主張は「レビューからは、ドキュメントからは分からないことが分かる」という点にある。ドキュメントから分かるのはライブラリが何をするかであり、レビューから分かるのはなぜその形なのか(試して捨てた 3 つの設計、回避を強いたコンパイラバグ、著者自身が今も疑っている判断)である。そして後者は、本からも、ウェブ検索からも、すべてを読んで何も作ったことのないモデルからも得られない。
shared_ptr / optional / variant / filesystem など標準入りした実績と、約 19,000 の公開リポジトリでの利用を示しつつ、Peter Dimov・Howard Hinnant・Beman Dawes ら担い手の名を挙げて「次に誰が継ぐのか」を問う。答えとして掲げるのは、新人が 5 秒で見切りをつけないよう入口を作り直すこと、ドキュメンタリー、そしてインターンとメンタリングであり、締めは「Boost は、なりたい技術者になる場所だ」。
リフレクション(C++26 静的リフレクション)
What can C++26 reflection do for you? — Andy Webber
「C++ をネットワークプロトコルの技術仕様そのものにする」ことを目標に、板情報プロトコルのパーサをリフレクションで丸ごと生成した実験レポート。
#pragma pack した構造体をワイヤ形式の定義として扱い、メッセージ構造体を「形」から発見し、ワイヤ上のレイアウトとメモリ上のモデルを分離し、アノテーションで構造体にポリシーを載せる(gcc 16.1 で検証)。
結論は両面ある — リフレクションは柔軟で、アノテーションは表現力があり、宣言的なコンパイル時 DSL をいくらでも作れる。一方でコンパイル時プログラミングの制約(構造型、推移的なコンパイル時確保のみ、即時関数)とそしてデバッグの難しさも同時に述べる。
A Duck Does More Than Quack: Synthesizing VTables with C++26 Reflection — Ryan Keane
共通基底を持たない複数の型を実行時に差し替えたいとき、継承は侵入的、std::function は operator() のみ、std::any にはインタフェースがなく、既存の型消去ライブラリはマクロが多い。いずれも決め手を欠く。
C++26 リフレクションによる rjk::duck は非侵入的・ゼロボイラープレート・同等性能を同時に達成する。define_aggregate で仮想関数テーブルを生成し、関数子を使って生成集成体から継承し、インタフェースメンバあたり 16 バイト → 8 バイト → ポインタ相互変換可能性と [[no_unique_address]] によって 0 バイトへと詰めていく。最終的にアセンブリは手書きの型消去と同じ 2 ロード、間接参照を 1 つ削れば 1 ロードになる。
著者は前年秋に C++ を学び始めた学部 2 年生で、朝ブログを書いたら午後に CppCon へ、2 時間後には WG21 提案へ招かれたという経緯も語られる。
Can I memcpy This Type Across a Boundary? — Fanchen Su
共有メモリ IPC、プラグイン/ホスト、スナップショット保存で「オブジェクトのバイト列を境界越しに渡せるか」をC++26 リフレクションでコンパイル時に検証する TypeLayout の発表。
鍵は独立した 2 つの検査が必要だということ — バイトコピー検査(この型はこのコピー方法の要件を満たすか)とレイアウト検査(このビルド間でレイアウトは一致するか)。is_trivially_copyable_v が true でもポインタを含めば失格であり、同じ long double が Linux x86-64 で 16 バイト、ARM64 macOS で 8 バイトという実例が両者の独立性を示す。ポインタは relative_ptr で領域先頭からのオフセットに置き換える。
結論は慎重で、6 ビルドでの検査通過は「この型・このビルド・このコピー方法についての承認」でしかなく、値の意味の妥当性・実データの検証・ストレージと寿命と同期の提供は依然としてアプリケーションの責任である、と明示する。
Zero Cost Scripting Languages for Game Engines With C++26 Static Reflection — Koen Samyn
「C++ を他言語のコンパイルホストにする」というビジョンのもと、AngelScript のバイトコードをC++26 リフレクションでコンパイル時に C++ テンプレート特殊化へ変換し、VM のオーバーヘッドをコンパイラに消させる試み。
技術の核は std::meta::substitute による特殊化の生成と、畳み込み式による命令呼び出しの合成。バイトコードは #embed で取り込み、NTTP として渡す。
10 回ループして加算する関数は mov eax,0x2d 1 命令に畳み込まれる(VM のディスパッチが完全に消える)。要点は 3 つ — 手法は成立する、生成コードはコンパイラがインライン化して最適化できる、そしてこの技法は他言語にも再利用できる。
The Address is Not The Place: Object Residency in C++26 — Laurie Kirk
L1/L2/L3 キャッシュ、仮想メモリ、AutoNUMA、CXL が与えてきた「アドレス=場所」という幻想は、NUMA・HBM・階層化メモリ(200ns と 2ms が同居する世界)では破綻する。
オブジェクトについて「誰が(所有権、スマートポインタ)」「いつ(ライフタイム、RAII)」を表す語彙はあるのに「どこに」がない、という指摘から wherency(配置に関わる事実を表明し、ポリシーが適切な常駐先を決められるようにする意味的性質)を造語する。Newton 的な「絶対で不動の場所」から Leibniz 的な「関係としての場所」への転換が主題。
実装としては階層対応のカスタムアロケータ hotpotato(Tier 0 ローカル〜 Tier 4 CXL、LRU による昇格・降格)を示し、コンパイル時の wherency は C++26 のリフレクションとアノテーション(P3394)で型に付与できる、と展望する。
【2. 並行・非同期・Sender/Receiver】
Concurrency for Modern CPUs — Lock-Free or Lock-based? — Fedor Pikus
CppCon 2017 で「もっとも要求の厳しい並行コードはロックフリーで書け」と述べた本人が、10 年後に「その結論はほぼ 100% 間違っていた」と自己修正する発表。機械も基礎も変わっていないのに何が変わったのかを問う。
共有整数のインクリメントを Granite Rapids・Apple M3 Pro・NVIDIA Grace で測ると、高競合下ではスピンロックがアトミック操作を上回り、lock-free(CAS)は wait-free より遅い。高競合の操作はキャッシュコヒーレンシプロトコル、とりわけ通信遅延に支配されるためである。
結論は「ロックフリープログラミングは死んだ。ロックフリープログラミング万歳」。すなわち最高競合という伝統的な主戦場はもはや強みではないが、クリティカルセクションを最小化する「ロックフリー的スタイル」は依然として必要で、低競合にはアトミック操作、高競合にはスピンロック、ドメイン間の受け渡しには DCLP、という使い分けに落ち着く。
What Are We Synchronizing? — Robert Leahy
「何を同期しているのか」を出発点に、値の意味によって必要な同期が変わることを論じる。
オブザーバビリティ用のパケットカウンタのように「値はただの値」なら memory_order_relaxed で足りるが、when_all で最初の例外だけを記録する場合は話が違う。変更順序、read-modify-write が変更順序上の直前の副作用を読むという保証、データ競合の定義、join による同期といった規格の文言で境界を引く。
結論は 4 点 — アトミック性と同期は別物である、同期は happens-before 関係によって与えられる、ロックはクリティカルセクションについて考えさせてくれる、そしてロックフリープログラミングはあらゆる病的な状態について考えることを強いる。
Implementing Async RAII — Robert Leahy
「コンストラクタはコルーチンであってはならない」「デストラクタはコルーチンであってはならない」という規格の制約のもとで、io_uring の OPENAT/CLOSE のように取得も解放も非同期な資源をどう RAII に載せるかを扱う。
Kirk Shoop の async-object(P2849R0)を踏まえ、enter_scope_sender / exit_scope_sender というコンセプト群と、リフレクションを用いた consteval メタ関数で within と enter_scopes を設計する。
結論は概念の組み替えにある — RAII は本質的にオブジェクトではなくスコープの話である。enter/exit スコープ sender がスコープ境界を非同期領域へ投影し、オブジェクトとは「スコープ+ストレージ」であり、lifetime が非同期操作に本質的に非同期なローカル変数を与える。
Senders, Receivers, and Robots: Structured Concurrency for Autonomous Navigation — Nahuel Espinosa
「ロボットに何かをさせたい。そして止められるようにしたい」という動機から始まる、ROS の自律走行スタックへの構造化並行性の適用事例。
「計画アクションを呼ぶ → 結果を制御アクションに渡す → コントローラに 60 秒タイムアウトを課す」という単純な要求が、コールバックベースの action_client API ではネストの山になる様子を見せ、sender/receiver で平坦に書き直す。
締めくくりは RPC を超えた実用の形 — トピックを購読し、イベントループを塞がずにワーカースレッドで重い計算をし、結果を publish する、という典型を continues_on でスケジューラを渡り歩くパイプラインとして示し、counting_scope とシグナルハンドラで停止と join まで面倒を見る。
Awaiters and Awaitables — Mateusz Pusz
コールバックベースの既存 API(OS タイマー、スレッドプールの完了通知、他人の std::future<T>、C ライブラリの関数ポインタ)とco_await を繋ぐ「awaiter」の書き方だけに徹底的に絞った発表。
明示的に「task<T> の話ではない」という立場を取る —std::execution::task<T> が C++26 に入るので promise_type は他人の問題になったが、awaiter だけは自分で書き続けることになる(2029 年になってもベンダ SDK は関数ポインタを取る)。
設計上の指針が 2 つ。awaitable が公開 API で、awaiter は機械であり、コンパイラだけが見るべき 3 つの関数である(operator co_await と await_transform がその分離を担う)。そして対称転送を選ぶこと — await_suspend の中で resume() を呼ぶと入れ子になり、1 万回ループすれば破綻する。「コルーチン連鎖のスタック深さは、ループで何回 co_await したかではなく機械の性質であるべきだ」。
Readable Async Workflows in Modern C++: Coroutines Meet std::expected — Futong Liu
注文処理(認可 → 読み込み → コンプライアンス確認 → コミット → 通知 → 応答)という同期なら一直線の処理が、コールバック化によって 4 つのファイルに散逸していく様子から始まる。
コルーチン + std::expected で一直線の可読性を取り戻したあと、議論はその先へ進む。when_all でバッチ処理すると「19 件成功・1 件失敗」が返り、戻り値の型に何を入れるかという設計問題が浮上する。答えは std::expected<LoadSummary, LoadError> のように、成功分と失敗分の両方を持つ型を設けること。
通底する原則は、実行方針(どう走らせるか)と結果方針(何を成功とみなすか)を分けること、そして上位層には局所的な詳細を隠し、下位層には判断に必要な文脈を残すという層の設計。
Lock-Free Timer Scheduling With C++ Atomics — Maxim Gurschi
「毎秒 4 更新なら 1 フレームの予算は 250ms、計算が 100ms なら 150ms は遊んでいる」という UI 更新を、独立インスタンス数千件ぶん捌くタイマスケジューラの設計。Mechanical Sympathy に則りロックフリーで組む。
効果は明確で、work stealing 系のベースラインに対し 100 万件の 1 サイクルのスケジューラ時間が 8.5 秒 → 0.015 秒(550 倍以上)、定常状態で動くワーカーコアは 8 → 1、プロセス CPU 使用率は 200% → 5.8%。ベースラインは 250ms サイクルに追いつけていなかった。
要点は 4 つ — スケジューラは仕事を繰り返し再スケジュールするのではなく所有権を移すことでスケールする、仕事を動かすのは有益なときだけ、正確である必要のない情報は同期しない、そしてホットパスの所有権移動と回収にロックは要らず、慎重にスコープを絞ったアトミック操作で足りる。
Tying up Loose Threads: Making Your Project No-GIL Ready — Charlie Lin
PEP 703 のフリースレッド(no-GIL)Python に対して、C/C++ 拡張をどう対応させるかの実務ガイド。
インタプリタ側の変更点を押さえる — アロケータの pymalloc → mimalloc と遅延回収、参照カウントの ob_ref_local / ob_ref_shared への二分割、コンテナのオブジェクト単位ロック、そして反復中の変更・read-modify-write・TOCTOU が非アトミックである点(Py_BEGIN_CRITICAL_SECTION で保護する)。
実務面では PyUnstable_Module_SetGIL による宣言、cibuildwheel の cpython-freethreading、-X gil=0 と pytest --run-parallel、TSan/ASan、そして pytest.warns や capsys などスレッド安全でないテスト機能の具体的な一覧が役に立つ。
締めは進捗報告 — PyPI の人気 wheel の 61% が既にフリースレッド互換。だから「依存を上げよう」。
Processor Design and C++ Memory Models — Ofek Shilon
「メモリ操作が可視になる順序はプログラム順序と異なりうる」という定型句の中身を、プロセッサ設計の側から説明する。
Writer/Reader の 2 コアによる data/ready の例で strict consistency と sequential consistency を対比し、「ではなぜ SC ではないのか」を問う。プライベートキャッシュは答えではない(キャッシュコヒーレンス、MESI がある)。
真の原因として invalidate queue、非 multi-copy atomicity、ライトバッファを順に示し、さらにパイプライン、アウトオブオーダ実行(レジスタエイリアステーブル、リザベーションステーション、リオーダバッファ)まで降りていく。C++ のメモリモデルが「なぜそういう形なのか」をハードウェア側から腑に落とさせるのが狙い。
You Shall Not Pass*! — Daniel Pfeifer
Sean Parent の "C++ Seasoning" へのオマージュとして 3 つの目標を掲げる —生ループをなくす、生の同期プリミティブをなくす(C++26 実行制御ライブラリによる構造化並行性)、生ポインタをなくす。
ただし Core Guidelines の I.11(生ポインタで所有権を渡すな)と F.7(一般用途ではスマートポインタでなく T*/T& を取れ)の名前の衝突を指摘する。Type* object を見て答えられない問い(delete する責任は誰か、いつダングリングするか、変更してよいか)こそが本質だとする。
結論は、ポインタは — スマートであろうとなかろうと — 関数シグネチャに現れてはならない。通常の値セマンティクスを持つクラスで包むこと。shared_ptr/unique_ptr は有用な部品だが、std::indirect/std::polymorphic はより良い部品であり、そして部品はまだ足りない。仮想関数が全部 const なら shared_ptr<Base const>、そうでなければ std::polymorphic<Base>(著者いわく「NVI 2.0」)。
Beyond the Scheduler: Designing Predictable Embedded Systems in Modern C++ — Elbert Dockery
「リアルタイムの推論はスケジューラで止まってはいけない」という主張のもと、C++20 で境界のある組み込みファイルシステムを自作した工学的ケーススタディ。
メタデータ・アロケータ・ジャーナルをブロックデバイス契約の上に構築し、C++20(std::array/std::span、RAII、constexpr ジオメトリ、操作カウンタ)で制約を明示的に符号化する。4096 バイトと 4097 バイトで要求される仕事量が 1R/9W と 1R/10W に変わる、という粒度で可視化する。
副産物として、境界を明示したことがメモリ問題も暴いた — create_file() のスタックフレームが12,536 バイト → 264 バイト(約 97.9% 削減)(代わりに FileSystem オブジェクトは 4.9KiB → 9.0KiB。メモリは消えたのではなく明示的で再利用可能になった)。
要点は 3 つ — 時間を測る前に仕事量を束縛せよ(タイミングは何が起きたかを語るが、仕事量モデルはなぜかを説明する)、資源要求を明示せよ、そして予測可能性は層をなす(スケジューラ=いつ走るか、アーキテクチャ=どれだけの仕事か、プラットフォーム=その仕事がいくらか)。
【3. 低レイテンシ・性能最適化】
Writing Low-Latency C++: Predictability, Cache, and the Architectures Underneath — Sampad Acharya
Bloomberg の債券取引チームによる低レイテンシ実装ガイド。「正しさは前提、良し悪しを分けるのは予測可能性」が主題 —「10% 遅くても予測可能なシステムは、10% 速くてもジッタのあるシステムに勝つ」。
STL コンテナ、メモリレイアウトとキャッシュ、アトミック操作とミューテックスの比較、スレッドとスケジューリングを順に扱うが、OS スケジューラの扱いが他書と異なる。ミューテックスの再取得では直近に走っていてキャッシュにデータが残っているスレッドが選ばれやすく、4 スレッドあっても A→A→A→A と同じワーカーが勝ち続け、B・C・D は走らず、キューは伸び、実質シングルスレッド化する。
締めは計測の心得 — 平均は嘘をつくのでパーセンタイルと分布を見ること、コンパイラフラグ・CPU・カーネル・環境を記録しなければベンチマークは無意味であること、そして「絶対的なベンチマークは幻想であり、結果は測定した環境の中でしか一般化しない」。
The Latency Slippage: Understanding the Hidden Costs of Caching for Low Latency C++ — Sanchit Gupta
高頻度取引会社のエンジニアによる、ハードウェアの「推測」を暴く発表。CPU は次に欲しいライン(Prediction)、誰がどの状態で持つか(Ownership)、何を残すか(Retention)をヒューリスティックで推測している。
Core i9-14900KS 上で各階層の相対コスト(1 : 3.2 : 14.5 : 103)や「1 コアが同時に抱えられるキャッシュミスは 16 スロットまで」といった事実を確かめながら、1 つのアプリを分解していく。clflushopt は読み捨てるリングに、clwb は他コアが読む直前のラインに適する(クリーンなラインの writeback は書くものがないのでほとんど動かない)。
結果は 1 イベントあたり 510ns → 329ns。しかも計算内容はまったく同じで、変えたのはキャッシュの振る舞いだけ。要点は 3 つ — プリフェッチ距離と対象はアプリの意味論と perf 指標から決めること、clflushopt は 1 命令でキャッシュを空けてストールを減らすこと、そして共有読み出しにはコストがあるので、他コアが読む前に clwb で L3 へ押し出すこと。
When Zero-Cost Abstractions Aren't Zero-Cost — Steve Sorkin
「使わないものには金を払わない。使うものは手書きより良くはできない」という Stroustrup の約束が成立する条件を明確にする。
核心は「ゼロコストは保証ではなく契約である」。抽象が最適化で消えるには、具体型が見えていること、制御フローが解析可能であること、抽象境界が透過であることが必要で、情報が隠されたり間接参照や動的な振る舞いが残れば、コストは実行時に残る。std::function(型消去による情報の喪失)、Ranges/Views(合成の複雑さ)、段階的コンテナ(所有権と実体化の意味論)と、コストの出どころが抽象ごとに違うことを実例で示す。
「-O3 にすればいいのでは」への答えは — 最適化レベルは抽象が消し去った情報を復元できないし、意味論が要求する仕事を除去できない。しかも無料ではない(コード膨張、命令キャッシュ圧迫、コンパイル時間、時に -O2 より遅い)。
Are You Smarter Than A Branch Predictor? — Michelle D'Souza
ゲームショー形式(聴衆への質問と景品の消しゴム投げ込み付き)で分岐予測を解説する発表。
分岐予測なしのパイプラインの空転から始め、Fetch 段で Branch Target Buffer を引く仕組みを見せる。現代のプロセッサは予測ミスでパイプラインをフラッシュし正しい経路から再実行するために 10〜20+ サイクルを失い(2.2GHz で 20 サイクル ≒ 9ns)、その経路でキャッシュミスが起きればさらにメモリレイテンシを払う。
締めは最適化の方法論 — 分岐を節約せよ(エラーコードのチェックを減らす、ただしモナディックな書き方にも同じ問題がある)。そして perf stat で IPC・分岐ミス率・キャッシュミスを変更の前後で定量化し、Celero や Google Benchmark で end-to-end を測り、本番に似たデータで測り、PGO で実際の分岐傾向をコンパイラに教えること。
The Greatest Misconception in Computer Science — Alex Dathskovsky
「O(1) < O(log n) < O(n) < … という表だけで速さを判断する」という計算機科学最大の誤解を扱う。
計算量の合成(O(N) + O(N²) + O(log N) ⇒ O(N²)、「アルゴリズムは最悪の部分と同じ性能しかない」)と償却計算量を確認したうえで、話は CPU の進化へ移る — パイプライン、アウトオブオーダ実行、分岐予測、SIMD、分岐レスプログラミング、キャッシュ、マルチコア。ヒストグラムを unordered_map と vector で実装して比べると差は歴然で、漸近計算量は同じでも実測はまるで違う。
そして「A から B の要素を順序を保って取り除く」という一つの問題に対し、ソート+二分探索、密なバイト探索、ビットマップ、Bloom フィルタ、ソート済みバケット探索、バイト集合バケット、ビット集合バケットと7 通りの実装を実測で比較する。結論は「力はあなたの手にある」— 計算量ではなくハードウェアに合わせて設計せよ。
What Is Your Algorithmic Core? — Egor Suvorov
「アルゴリズムはテストしにくく、頑固で、バグは表に出るまで隠れている」という観察から、コードからアルゴリズム部分を抽出せよと説く。
2 種類のバグを対比する。ラムダの参照キャプチャによる use-after-free のようなエンジニアリングのバグは行カバレッジで捕まるが、KMP による部分文字列探索のバグは違う — LLM が生成した 17 個のブラックボックステストで行・分岐 100% カバレッジを達成しても、1 行消してもテストが落ちない。
後半は仕様の不足を列挙する — 「最長増加部分列」と気づかずに貪欲法で誤る例、非機能要件(コールバック数、確保回数、償却か最悪か、「線形」とは何の線形か)、出力引数の追記か上書きか、自己部分オブジェクト代入。そして「正確な契約が不可能なこともある」と認める(CSV のエスケープ、ファイルシステムの可視性、SQL の既定分離レベル、浮動小数点の印字)。
Same Bits Without Losing MIPS: Reproducible Numerics at Full Hardware Speed — Andrew Drakeford
ハードウェア・スレッド数・命令セット・ツールチェーン、さらには実行ごとに答えが変わる問題に対して、達成困難な目標を立てて挑む —llama2.c を Apple M4 / Ryzen 9 7900X / RTX 5070 上でビット単位再現可能に、かつ遅くせずに動かす。
結果は 13 構成・0 不一致(1 実行あたり 12,864 チェックポイントをハッシュ)で全構成が同一トークン列。しかも性能はむしろ向上する(Ryzen AVX2 1 スレッドで 131 → 479 tokens/s)。手法は非決定的な数値要素を切り出してバックエンドを差し替え可能にし、契約を「式を固定し、実行を変化させる」と定めること。
主張は標準化に向かう。決定的な数値契約が規定すべきは、要素演算の意味、評価すべき式または厳密な中間状態、そして観測すべき結果。規定するのは表現と演算意味論と観測間の整合性だけで、スレッド数・スケジューリング・分割・タイリング・CPU/GPU の実現は自由にしておく。P4016R0(canonical reduction)と P4229R0(canonical scan)として提案中。
Death by a Thousand Tape Entries — Jorg Lotze
金融・科学計算向けの随伴自動微分(AAD)で、テープのオーバーヘッドを削る 10 個の設計ルール。
CVA 評価(モンテカルロ 10,000 パス、40 入力)では、バンプ&再評価が 41 倍、ナイーブなテープの AAD は 55 倍でバンプ&再評価にすら負けるのに対し、XAD は 3.9 倍(理論限界は 4 倍)。まとめは「55 倍が 3.9 倍になったのは、テープの 1 エントリを安くし、そのうえでエントリの 3 分の 2 以上を削ったから」。
ワークロードによる差も分析する(LIBOR 6.7 倍、Heston 2.9 倍)— 前者は primal が 1 サイクル 4 命令で余裕がなく、後者は除算と exp の完了を待つため、その待ち時間にテープの処理が収まる。実証は QuantLib(30 万行、Real を XAD の型に束ねただけで書き換えなし)で、69 個の感応度がバンプ&再評価の 69 倍に対し 1.3 倍。
std::simd Without Compromise: SIMD in C++26 and Beyond — Ruslan Arutyunyan, Daniel Towner
30 年以上プロセッサはスレッドではなくデータ並列性で並列能力を高めてきたのに、C++ にはそれを表現する標準的で可搬な語彙がなかった — C++26 でそれが変わる。
SIMD がパイプライン税(fetch/decode/schedule/speculation/retire)を償却する仕組み、3 つの実行次元(垂直/map、水平/reduce、shuffle/permute)、各レジスタ幅とマスキングを押さえたうえで、std::simd の実用形(unchecked_load と partial_load+mask_from_count による端数処理)を示す。
可搬性を掲げつつイントリンシックからの逃げ道も用意するのが現実的で、C++26 ではネイティブ幅の組み込み型との直接変換、C++29 では任意幅を扱う chunked_invoke と共通イントリンシックのラッパを提案する。rebind_cast は simd に限らず std::array や std::complex にも使える語彙として提示される。実装 xvec はオープンソース。
musttail-ling Our Way to a Faster Python Interpreter and New JIT Compiler — Ken Jin Ooi
インタプリタという構造は言語処理系だけでなくパーサ、シリアライザ、正規表現エンジン、状態機械全般に現れる、という導入からCPython 3.14-15 の [[musttail]] インタプリタと新しい JIT コンパイラを解説する。
従来の switch-case 方式(1 命令あたり 2 ジャンプ)、computed goto 方式(1 ジャンプだが 12,000 行超の巨大な単一関数になり、レジスタ割り付けを制御できず、コンパイラバグを踏みやすい — LLVM/GCC の実際の issue 番号付きで列挙)の問題を示し、末尾呼び出し保証によるディスパッチへ移行する経緯を辿る。
Modern C++ Techniques to Reduce Boilerplate Without Sacrificing Performance — VISHNU G NATH
イテレータの明示的ループのうち本質は Process(*it); の 1 行だけで、残りは「言語の儀式」である、という指摘から始まる。
「ポインタ+サイズ → std::span」「nullptr → std::optional」「イテレータループ → Ranges」「SFINAE → Concepts」という対応表を示し、中心的な主張を「モダン C++ は複雑さを取り除くのではなく、開発者からコンパイラへ移動させる」と置く。各技法は「何を得て、何を払ったか」を実行時間・コンパイル時間・アセンブリ・バイナリサイズの 4 軸で測る(そのために CompBenchmark という計測フレームワークを自作している)。
導かれるのは 11 の工学原則 — 意図を書け機構ではなく、共通パターンを標準化せよ、不正な状態を無視できなくせよ、状態を型システムでモデル化せよ、資源の寿命をオブジェクトの寿命に結びつけよ、そして最後が「最適化の前に測れ。証拠は直感に勝る」。
Practical HPC — Forcing the Compiler's Hand with Modern C++ — Marco Barbone
FINUFFT の手作業アンロール(issue #459)から生まれた、「性能を買うかわりに毎日利息を払う 6 つのパターン」の実測比較。手動アンロール、マクロ、#pragma、手書きの if 連鎖によるディスパッチ、インライン asm、コード生成を順に検証し、#pragma GCC unroll が速度向上ゼロ(ループは展開されてもリダクションは展開されず、浮動小数点加算は結合則が成り立たないため再結合が許されない。警告も出ない)といった落とし穴を暴く。
後半は同じ性能を標準機能で取り戻す — C++17 の integer_sequence と畳み込み式、C++20 のテンプレートラムダ、C++26 の template for、畳み込み式によって if 連鎖を書かずに済ませるディスパッチ、そして std::simd。
最終的に ISA 名がソースに一切現れないまま、手書きアセンブリ(25.9 倍)の 6% 以内の 24.6 倍に到達する。
What 25 Days of Optimisations Taught Me About Compilers — Matt Godbolt
「Advent of Compiler Optimisations」24 本の記事と動画の総括。結論は「コンパイラが賢くないのではなく、情報が足りないのだ」。string_view の比較が重なった 4 バイト読み 2 回に化ける例、再帰 gcd がスタックを使わないループになる例から、Counter<int> と Counter<long> で後者のほうが速い(int 同士はエイリアスしうるので毎回ストアが必要)というエイリアシングの話から、++num_compares を 1 行足すだけで strlen がループ内に戻る話、gcc の部分インライン化まで。教訓は「許されていなかった(規格を確認せよ)」「知らなかった(情報を与えよ)」「そう判断した(測定せよ)」の 3 分類。
企画中に実際に報告したコンパイラバグ(gcc #122315 ほか)を再訪し、バグ報告を呼びかける。
"If You Can't Measure It, You Can't Improve It": From Profiling to Automatic Benchmarking — Kris Jusiak
linux-perf を軸にした計測手法の総覧。「決して throw されない try/catch を足したら?」といった "what if" ルールで問題意識を作り、各階層の実測コスト(L1d 0.75ns、分岐予測ミス 3.50ns、DRAM 70ns)を示す。
Top-Down マイクロアーキテクチャ解析、サンプリング(perf + FlameGraph)、インストルメンテーション(bpftrace、llvm-xray)、トレーシング(Intel PT)、機械語解析(llvm-mca)、シミュレーション(callgrind)、ベンチマーキングを順に実演。
後半は「間違ったものを測れば正しいものは改善できない」としてコード配置由来のバイアスとノイズを示し、angr と Unicorn によるシンボリック実行で全経路を非侵入的に計測する Symbolic Benchmarking を提案する。
From 20 Nanoseconds to One: Optimizing Bishop, Rook, and Queen Move Generation in a Chess Engine — Aryan Naraghi
自作チェスエンジン FollyChess における、スライド駒の指し手生成の最適化記録。Bitboard(駒の存在を表す 64 ビット整数)と素朴なレイ探索(ルーク 22.6ns)を出発点に、4 つの手法を検討する。
総当たり表は約 9.44 ゼタバイト(世界のストレージの約 10%)で却下、relevancy mask + ハッシュマップは 2.5MB だがホットパスにハッシュが乗り 6.6ns、PEXT は速いが x86 限定。
最終的にマジックビットボード(占有 × 乱数マジックの乗算シフトハッシュ)で 0.9ns・約 24 倍・2.1MB・可搬を達成する。マジック候補が「3 つの乱数の AND」で疎に生成される理由も解説。ただし Amdahl の法則により探索全体では 1.06〜1.30 倍にとどまることも示す。
【4. ビルドシステム・モジュール・ツールチェーン】
CMake: Making Hard Things Easy — Bill Hoffman
1999 年に CMake を作った Kitware 創業者・CTO による発表。「難しい問題 → 専門家チーム → オープンソースの解」という働き方の中で、ビルドもその問題の 1 つだったという経緯を語る。
CMake の 4 つの時代(2000 年のコンパイラフラグの寄せ集め、2004 年の普及、2014 年の target_* コマンド、2025 年の相互運用と標準化)を辿り、1 つのコンパイラコマンドからビルドエコシステム全体へ、そしてビルドシステムの生成からツールチェーン全体が消費できるフォーマットの定義へという流れを示す(CPS、SBOM、モジュール、計装、リモート実行)。
行動喚起は 4 点 — 要件をターゲットでモデル化せよ、下流の利用者に必要な知識を渡せ(CPS を publish し、モジュールを試せ)、ビルドを計測してボトルネックを見つけよ。いずれも「専門知を一度書き留め、再現可能にし、どこでも動き続けるようにする」という一点に帰着する。
Building C++20 Modules: The Rest of the Story — Vito Gamberini
C++20 モジュールを「どうビルドするか」に特化した発表。各 TU が異なるマクロ定義・インクルードパス・さらには異なる -std= を持つという現実から問題提起する。
翻訳単位の分類(モジュール単位でない TU、ヘッダ単位、名前付きモジュール単位、パーティション、実装単位)を整理し、具体的なコマンド例と、現在の一般的な合意手法、そして未解決の問題を扱う。
価値があるのは終盤の落とし穴集 — インタフェースのみのライブラリでundefined reference to 'initializer for module math' になる件、パーティション実装単位で初期化子が多重定義になる件、import :detail; した型が「math から import されていなければならない」と怒られる件など、実際に踏む地雷が再現コマンド付きで並ぶ。ビルドシステム側の答えとして CPS の cpp_module_metadata にも触れる。
Using Modules in a Real Project — Erez Strauss
「C++ モジュールは使えるが、統合には手間がかかる」という立場からの実務レポート(C++26・CMake・Clang・GCC)。tally という台帳ライブラリで、テンプレート・アウトオブライン定義・マクロ・ヘッダオンリー依存という「最初の 1 週間の壁」を実演する。
#include と import の違いを表で整理し(マクロは渡らない、BMI は既にパース済み、インタフェース BMI はビルド順の辺になる)、CMake 側の作法(project() の前に固める、FILE_SET CXX_MODULES、Ninja、import std、非モジュールターゲットではスキャンを切る)を示す。
「上司にこう言え」というまとめが実務的で — GCC 16 はまだ -fmodules が必要でプライベートモジュールフラグメントがない、CMake の import std は実験的、BMI はコンパイラ・フラグ・標準ライブラリをまたいで可搬ではない(だから .pcm ではなくソースを配布する)、全 C++20 ファイルのスキャンは実コスト、include-after-import は一回限りでなくバグの一類型。結論は「モジュールは 2025 年頃に実験的でなくなり、エコシステムは使える。ただし、手間をかけずに済むところまでは来ていない」。
My DAG Ate All My Cores: Identifying and Resolving Build Graph Bottlenecks — Florent Castelli
「128 コア買ったのにビルドは最大でも 40 コアしか使っていない。問題はマシンではなくグラフだ」が出発点。コードベースをファイルの階層ではなくターゲットの有向非巡回グラフとして見る視点を与える。
おなじみの苦痛をグラフの問題として言い換える —「ヘッダ 1 つで 400 ファイル再ビルド」→ ファンインの大きいノード、「96 コアの CI が半分遊んでいる」→ クリティカルパスが長い、「リンク順が問題になる」→ 認めていない循環。ハードウェアを増やして速くなるかは 3 つの数字で決まる。総作業量 ÷ クリティカルパスがコア数を下回るなら、次のマシンは遊ぶ。
対策は速度ではなく形を変えること(パスを短くする、辺を消す、畳む。PRIVATE 1 つで形が変わる)で、変えるたびにスラックが移動するので測り直す。そして退行防止として、クリティカルパス予算・ファンイン上限・禁止辺・並列度下限を CI でラチェットする。ツール NinjaScope は .ninja_log から単一 HTML レポートを生成し、標準ライブラリのみで依存なし。
Works on My Machine, Works in CI: Treating Your Build Toolchain as a Dependency — Luis Caro Campos
requirements.txt や cargo.toml に相当するものが C/C++ には長らくなかった、という話から始め、vcpkg・Conan・apt のワークフローを比較する。
主題は ツールチェーン自体を依存関係として宣言すること。ツールチェーンは往々にして手動セットアップで開発者ごとにバージョンが微妙に違い、しかも自動 SBOM 収集からも漏れている。Conan では [tool_requires] に gcc/13.1.0 や msvc/19.44 と書くだけでよく、プロファイルが「ターゲット(host)」と「ビルドする機械(build)」を分けて持つので、クロスビルドも自然に表現できる(macOS から Linux/aarch64 へ、など)。
利点と欠点を両方挙げるのが誠実で、ツールチェーンのドリフトが「Conan プロファイルのドリフト」に変わるだけだが、そちらには既に道具がある。ただし商用ツールチェーンでは EULA を確認せよ、IDE 側の制約もありうる、と断っている。
Component based or monolithic development for large C and C++ projects: Why not both? — Diego Rodriguez-Losada Gonzalez
Conan の作者による、モノレポ型とコンポーネント型(マルチレポ)開発の対立を解きほぐす発表。Conway の法則から説き起こす。パッケージが進化したときの問題を 2 つに分ける — API 互換な変更は CI の問題、API 非互換な変更は開発者ワークスペースの問題。
CI 側では効率・高速・安全・保守可能という条件を立て、「パッケージのマルチリポジトリ=ソースのマルチブランチ、プロモーション=マージ」という対応を示し、消費者側から辿る方法はスケールしないので「組織の products を選び、各 product の依存グラフをロックファイルで計算し、統合して必要なビルドを順序付きで返す」方式(conan graph build-order、GitHub Actions 200 行で実装可能)を提案する。
ワークスペース側は editable パッケージと、オーケストレーション型・スーパービルド型・動的ワークスペースの 3 方式を比較する。
Automatic Splitting of Translation Units: Compiler-Agnostic Compiler Frontend Parallelization — Damien Buhl
「ビルドはグラフであり、コンパイルエッジは原子である」— その内側はビルドシステムにもキャッシュにもスケジューラにも見えない。unity build が原子を大きくする(融合)のに対し、本発表は逆向きに関数定義 1 つにつき 1 ピースへ分裂させる。
cpp-splitter は libclang で TU を書き換えてピースを並列コンパイルし、ld -r で元どおり単一オブジェクトに再結合する。CMAKE_CXX_COMPILER_LAUNCHER として透過的に挟まるので、ビルドシステムからはエッジが 1 本に見えたままになる。
C++20 モジュールにも適用する — -fmodules-reduced-bmi でも clang は ODR ハッシュを BMI に格納するため本体編集で全 importer が再ビルドしてしまうが、宣言のみに書き換えた interface をプリコンパイルすれば BMI をバイト単位で同一に保てる。
ベンチマークは Boost.Spirit、OpenCV、p4c をリモート実行クラスタ(-j500)上で計測し、バイナリ等価性も検証する。
Running Compiler Explorer in 2026: Sandboxes, Storage, and Strangers' Code — Matt Godbolt
2012 年に「range-for は遅いかもしれないから使うな」と言われたことに始まる、15 年続く Compiler Explorer の運用記(結論のスライドは「実は range-for のほうが速い」)。現在は週 100 万回超のコンパイル、14 年以上有効な恒久短縮リンク。
サンドボックスでは、LD_PRELOAD による open の差し替えから始まり、-specs= を使った実際の攻撃(/tmp が noexec でも通る)がフラグのブラックリストを生んだ経緯、Docker を退けた理由、firejail を経て nsjail に至る多層防御を語る。
ストレージではコンパイラ 6,236 個(2.4TB)を NFS から squashfs ベースの cefs へ移し、autofs による遅延マウントでマウント数 2,182 → 約 30、起動時のマウント時間 30〜50 秒 → 18 秒に短縮した過程を示す。
最後に Compiler Explorer, LLC の収支(年間 収入 9.4 万ドル / 支出 5.3 万ドル)も公開する。
Prioritizing Pretty-printers — Braden Ganetsky
WG21 書記でもある LSEG のエンジニアによる、GDB の pretty-printer を「書く・配る・テストする」まで通した発表(本人いわく「C++ そのものよりツーリングの話」)。
セットアップ(.gdbinit、カスタム printer の読み込みと配線)、to_string() と children() による基本的な書き方を押さえたうえで、配布では install ルールに入れてもなお利用者が source printer.py を要求される問題を指摘し、auto-loading を導入する。
テストでは、Boost.Unordered が所定のブレークポイントで表示を目視確認する手作業だったことを出発点に、libc++ の方式を下敷きにした CTest 互換の自動テストライブラリ k3::gdb を構築する。
締めは「私たちの仕事はソースコード以上のもの、人に関わるものだ」。
組み込み・リアルタイム
Memory Safety on a Microcontroller: What Embedded C++ Teams Can Actually Do Today — Darijo Topic
Cortex-M33(MMU なし)・Zephyr 4.4・GCC 14.3 という具体的な環境で、組み込みチームが今日実際にできるメモリ安全対策を扱う(全 109 枚、数値はすべて Compiler Explorer で再現可能)。
議論の前提として PC / Linux SOM / マイコンの三列比較表を示し、既存のメモリ安全性文献が前提としない第 3 列(RAM 数十〜数百 KB、フラットな物理アドレス空間、OS は自分自身、更新は届かないかもしれずブリックは出張修理、障害時は物理的被害、デバッグはフォルトハンドラとオシロスコープ)で話す、と宣言する。
対策の提示の仕方も定めていて、コスト・効果・適用条件の 3 つを揃えて語れという —「コード変更ゼロ、チェックあたり 10 バイト、範囲外書き込みを防ぐ」。CISA のロードマップ要件に対しても、Rust について「まだ。毎年見直す。理由はこれ」と明示的に答えよと説く。締めは 6 段のループ — 知る → 検査する → 制約する → 測る → 観察する → 繰り返す。「それがロードマップであり、チェックリストはその時点のスナップショットにすぎない」。
AEMBER: Modern Embedded C++ Without the Chaos — Arian Ajdari
1.1MB・C++26・起動 10 秒未満・P2996 リフレクションを使う PID1(OS が最初に起動するプロセス)バイナリ AEMBER の紹介。「組み込みのアプリロジックを書きたいのに、init システムのブートストラップと配線とデバッグに時間を取られる」という問題設定。
PID1 の責務(孤児プロセスの回収、SIGTERM の処理、クラッシュすればカーネルパニック)を押さえ、signalfd + epoll による取りこぼしのないシグナル処理、LXC コンテナを第一級の監視対象とするオーケストレーション、Qt ダッシュボードによる死活監視を解説する。数字は実測で、1.1MB バイナリ・RSS 6.6MB(対する systemd は RSS 30〜50MB・C 150 万行のモノリシック)。
要点は 5 つ — C++23/26 は組み込みで実用段階にある(std::expected、jthread、stop_token、P2996)、PID1 には sigwait より signalfd が適し、signalfd と reaper スレッドの二重回収で取りこぼしを防げる、LXC + squashfs は軽量な組み込みコンテナになる、そして「開発者体験は機能である」(devcontainer + QEMU で 5 分で貢献できる)。
From Firmware to Screen: Real-Time Control, Simulation, and Visualization in C++23 — Kevin Gomez
リアルタイムシステムが「スケジューリング・シミュレーション・運用・可視化」の 4 つの道具の継ぎ接ぎになっており、バグは継ぎ目で生じるという問題提起から始まる —アルゴリズムは 2 回書かれて乖離し、テレメトリのフィールドは 3 回定義され、時間の意味は各ツールで異なり、テストはツール境界で止まる。
解決は、すべてを 1 つの共有 C++ ランタイム上のコンポーネント(SW_MODEL / HW_MODEL / DRIVER / SUPPORT)として構築し、全コンポーネントに同じライフサイクル・スケジューリング・パラメータ・コマンド・テレメトリを与えること。
説得力は実機デモにある — NUCLEO-F767ZI 上の誘導則を 20Hz で動かし、lidar は専用線で実シリアルフレームを 10Hz で送り、地上局から LED 点灯・経路実行・途中停止・lidar 切断(2 秒以内に sensor lost で停止)まで操作する。ボード側の最近傍距離は 1,915 サンプル中 1,867 でプラント側と一致し、残りも 1 スキャン遅れているだけだった。
Capability Routing Grid: From Decoupled Plugins to the Hardware Ceiling — Cyril TISSIER
Ubisoft が公開した C++17 ヘッダオンリーのツールキット GridZero の発表。厳密に分離されたプロジェクトをまたいだ疎結合な合成を目的とする。NodeLink は実装が中央レジストリも Init() 呼び出しもなしに、DLL 境界を越えてさえ自己登録する仕組みで、これだけでプラグインシステムが作れる。
もう一方の capability router は、振る舞いをモデル型に結び付けるのに仮想関数テーブルではなくフラットなテンソル参照を使い、仮想メソッドを持たない契約なら仮想呼び出しゼロ・ヒープ確保ゼロで済む(「ハードウェア限界まで」はこの意味)。
構成は Discovery / Models / Capabilities / Type erasure の 4 層で、リポジトリにはフレームワークを層ごとに積み上げる 9 段階の実行可能なテストステージが含まれる。
【5. Back to Basics】
Back to Basics: Move Semantics — Ruslan Arutyunyan
C++ 委員会 SG1 議長による ムーブセマンティクスの基礎。まず C++11 が追加したものを確認する(右辺値参照、拡張されたオーバーロード解決、標準ライブラリ対応)。
a、a = b、1、a + b、a++、++a、foo()、bar()、"hello" といった式を 1 つずつ lvalue / rvalue に分類していき、式のカテゴリを体で覚えさせる(すべて Compiler Explorer のデモ付き)。
終盤は完全転送で、参照の折りたたみの規則(T& & → T&、T& && → T&、T&& & → T&、T&& && → T&&)を導出し、まとめは 3 点 — 転送参照は型推論を伴うジェネリックなコードにのみ現れる、転送参照が std::forward を可能にする、そして std::forward は条件付きキャストである。
Back to Basics: Lambdas, Function Objects, and std::function — Roth Michaels
「関数とは本当は何か」という問いから始まる。数学の関数は副作用のない写像だが、コードでは効果のためだけに存在するものも関数と呼び、C++ では少なくとも 6 種類のものが「関数」と呼ばれる。
そこから関数ポインタ・メンバ関数ポインタ、operator() を持つ関数オブジェクト、より親しみやすい顔をしたラムダ、型消去としての std::function とそのコストへと進み、著者が実際にリリースしてしまったバグも共有する。
おまけの Objective-C ブロックとの比較まで進む。^{ } は [=] に相当し、捕捉はデフォルトで値かつ const、__block だけが例外。ブロックリテラルはスタック上にあるので保存には Block_copy(または ARC)が必要になる。そして self を捕捉すると retain される — これは shared_ptr を捕捉したときの循環とまったく同じバグで、__weak/lock() という同じ直し方をする。
Back to Basics: Function Overloading — Chris Ryan
WG21/EWG メンバによる関数(および演算子)オーバーロードの基礎。「コンパイル時多相 — 呼び出しの引数をシグネチャに照合して、コンパイラが呼ぶべき関数を選ぶ」という定義から入る。
引数の個数・型・順序が違う 3 パターンを実例で解説し、規則と落とし穴を押さえる —戻り値型はオーバーロードの区別に使えない、デフォルト引数は曖昧性を生む。
後半は演算子オーバーロード、とりわけ変換演算子に紙幅を割く。レガシー C 関数に渡すための operator const char*()、スマート参照の operator T*()、そして explicit operator bool() が if(db) を許しつつ int x = db; をエラーにする例。「単純化による複雑性の削減」が著者の信条。
Back to Basics: Code Analysis — Alexsandro Thomas
湿度データの統計を出すだけの小さなプログラムを題材に、静的/動的解析を学ぶ。「4 件読んだのにヒストグラムは 3 件」「空入力が arm64 では 0 終了、x86-64 では SIGFPE」という実演から未定義動作を導入し、Rice の定理により「どんなツールもすべてには答えられない」という限界も明示する。
終盤には AI による解析の実測比較まである。決定的なツールが報告しなかった指摘(平均値の暗黙の切り捨てなど)を AI は見つけた一方、人間のレビューは 118 行で 18〜53 分・20〜80 ドル、AI は 40〜120 秒・0.05〜0.43 ドル。それでも「人間のレビューはどのモデルにも出せないもの — 設計への反論、文脈、当事者意識 — をもたらす」と結ぶ。
結論の表では、各手段が「何を証明するか」と「何については何も言っていないか」を並べる —警告なしは「有効にしたチェックが発火しなかった」だけ、カバレッジ 100% は「全行が走った」だけ、PR 承認は「レビュアーが問題を見つけなかった」だけ。どの一手も他を置き換えない。
Back to Basics: std::unordered_map — Kevin Carpenter
「何十年も std::vector と std::map の 2 つで C++ を教えてきた。それは間違いだ」— 1,000 万要素で map の検索は約 22 倍遅い、という導入。
まず両者がいかに似ているか(宣言行より下は一字一句同じ)を示し、だからこそ 1 行の差し替えが間違いやすいと警告する。内部は「ハッシュ → 剰余 → バケットを走査」だけであり、バケットはヒープノードの連結リストの先頭で、要素ごとに 1 確保、アドレスは散らばる。そしてこのレイアウトは手抜きではなく、バケット API と参照の安定性を守る限り他の適合設計はありえない。
規則は 4 つ — 出力順に依存する map を差し替えない、ハッシュは識別に使う全フィールドを hash_combine する、自作キーでは Hash と KeyEqual が一致していなければキーを失う、サイズが分かるなら reserve() を呼ぶ。加えてスレッド安全ではない(同時 read+write は UB)。結論は「『念のため』で std::map を使うのをやめよう。unordered_map から始めて落とし穴を押さえ、読み取り主体で一度きりのロードなら flat map を検討せよ。そして測れ — コンテナがボトルネックであることは稀だ」。
Back to Basics: Memory Alignment — Sarthak Sehgal
CPU のメモリ階層(レジスタ ~1 サイクル、L1 ~4、L2 ~12、L3 ~40、RAM ~300 サイクル ≒ 100ns)から始め、キャッシュラインという転送単位、時間的局所性と空間的局所性、int x = array[5]; で 64 バイトが運ばれる様子を追う。
そこからコンパイラのレイアウト(パディングとパッキング)、alignas/alignof/sizeof/std::align による制御へ進む。
終盤の主役はフォルスシェアリング — 別コアのスレッドが同じキャッシュラインの別変数を書くと、互いに無効化を繰り返してラインが往復し続ける。alignas(64)(C++17 なら std::hardware_destructive_interference_size)で各変数を独立したラインに置けば解決する。まとめは、CPU はアラインされたアドレスで最も効率よく動く、キャッシュライン境界をまたぐと遅い、構造体のアラインメントは最大メンバのそれになる、packed 構造体とビットフィールドは空間を節約するが扱いに注意が必要で、packed メンバへのポインタは「packed である」という情報を失うので memcpy を使え。
Back to Basics: Text Formatting — Ben Saks
C の <stdio.h>、C++ の <*stream> / <iomanip> という既存手段を振り返ったうえで、<format>(C++20)と <print>(C++23)が何を改善したのかを示す。
構成は I/O の基礎、引数インデックスと動的な書式文字列、そしてユーザー定義型の書式化。printf の書式指定子の復習から入り、std::print が何を引き継ぎ何を変えたかを対比する。
後半は自作の formatter を最後まで書き切る。分数型に {:>3}/{:>4} のような幅指定を与えるために、parse が std::tie でイテレータを進めながら幅を読み、/ がなければ std::format_error を投げ(コンパイルエラーになりうる)、parse は必ず閉じ } を指すイテレータを返さなければならない(さもないと「閉じ } が見つからない」と言われる)。そして実行時に組み立てた書式文字列を std::vformat_to に渡す、という実装まで辿り着く。
Rule of 0,1,2,5,6,7,8,9,10? — Jason Turner
C++ Weekly の Jason Turner による、特殊メンバ関数と「〜の規則」の総ざらい。「調べ始める前は分かっているつもりだったが、気づいたら思いのほか深入りしていた」という導入から、特殊メンバ関数はいくつあるのかを問い直す。
結論は時代ごとに整理される。特殊メンバ(C++11)は「rule of 0」を優先し、資源を管理するなら 6 つすべてに賢く対処し、&& 修飾の代入演算子を提供しない(C++29 で A& operator=(const A&) && = default のような宣言は禁止される方向)。比較(C++20)は <=> か == のどちらかを常に default とし、自前の <=> を書くなら == も書く。後置演算子(C++29)は前置を定義したら後置を default し、[[nodiscard]] を付ける。
そして全演算子に共通する助言として、明示的に定義・default したものは[[nodiscard]]・constexpr・文脈次第で noexcept を検討せよ。一言でのまとめは「特殊メンバ・比較・前置演算子について、意図的であれ」。
Back to Basics: C++20 Concepts — Amir Kirsh
CoreCpp カンファレンス共同主催者による「Concepts 101」。制約、まともなコンパイルエラー、SFINAE に代わるオーバーロードの区別、そして「契約による設計」に近い強制力のあるドキュメントという利点から入る。
「反復可能な型」と「それ以外」で print をオーバーロードする課題を、enable_if_t による SFINAE 解(動くが 🤮)と比較しつつ、requires 節・template<std::ranges::range T>・abbreviated function template の 3 つの書き方で解いていく。
標準の Concepts ライブラリを概観し、concept Meowable = requires(T t) { t.meow(); }; のような自作コンセプトとif constexpr(Meowable<T>) としての使い方まで進む。
「Concepts は純粋なダックタイピングであり、特定の実型・基底クラス・インタフェースを要求しない」という位置づけを強調する。
【6. ライブラリ設計・API 設計】
Why Great C++ Libraries Fail (And How to Fix That) — Mateusz Pusz
「技術的卓越性 ≠ プロジェクトとしての卓越性」が主題。最先端のテンプレートと concepts を持ちながら最先端の苛立ちも提供しているライブラリを「要塞」と呼ぶ(入りにくい、理解しにくい、防御的なメンテナ、CONTRIBUTING.md も good first issue もない)。
ユーザージャーニーを Discovery から Community までの 6 段階に分けて 11 の施策を示す。命名は技術的判断である(units のような一般名を避ける。自身の mpusz/units が nholthaus/units と競合した経験)、リポジトリ名・CMake ターゲット・パッケージ名・名前空間を揃える、README の 5 秒テスト。
メンテナ側への助言も厚く、PR レビューではまず感謝し、設計判断を説明し、拒絶ではなく成功へ導くこと(「説明のない No はすべて、次の貢献を遠ざける」)。そして「ドキュメントを書くと API の設計欠陥が見える。説明しにくいなら、その API はたぶん間違っている」。行動喚起は「11 個を一度にやるな。今週ひとつ選んで 2 時間使え」。
Ranges Without Compromises: Designing for Simplicity, Performance, and Composability — Oleksandr Bacherikov
生ループ/アルゴリズム/range views を同じ問題で比較し、views が約束した「再利用可能で合成可能なアルゴリズム」がどこで崩れるかを示す。v | drop_last(2) | transform(fn) | filter(check) という一見きれいなパイプラインにも、fn の重複呼び出しや size < 2 のときの挙動という穴がある。
技術的な中心は式の書き換え規則で、reverse(filter(x)) を filter(reverse(x)) に、transform(f1) | transform(f2) を transform(f2∘f1) に、filter(p1) | filter(p2) を filter(p1 and p2) に簡約する。欠点も挙げる。STL にない技法なので教育が必要で、簡約後は型が変わり、N 個の view に対し N² 通りの組み合わせを扱う必要がある。
行動喚起は踏み込んでいる —「std::ranges::views を使うな」(range アルゴリズムは問題ないが、合成可能ではない)。本番には Flux、実験には rivers。そして view を増やすより内部イテレーションの標準化に資源を割き、標準コンテナとアルゴリズムをイテレータからインデックスへ移す道を議論すべきだ、と主張する。
Beyond Monads: Pattern Matching Alternatives for Result Types — Vitaly Fanaskov
std::expected<T, E>、std::optional<T>、std::variant<L, R> はすべて直和型であり、std::variant で実装できる。この整理が議論の土台になる。
.and_then(...).or_else(...) が好ましい場面がある一方、モナディック操作が冗長になる場面を挙げる —トレースやログ、別種の API へオブジェクトを渡す、オブジェクトやエラーを直接印字する、非同期 API、Qt の signal/slot、ユニットテスト。いずれも「中身を取り出した後で使わない」ケースで、std::visit 相当のほうが素直に書ける。
結論は 3 段構え —結果型に std::visit を適用できる(ただし別の名前空間のヘルパとして)、取り出した値をその後使わないときはより単純で明示的なコードになる、そして本来の最善手はパターンマッチングだが、提案の実現は「とても、とても遠い」ので、多くの人は当面使えない。
Using Type Erasure to Extend APIs You Don't Own — Jan Wilczek
オーディオプラグイン開発を題材に、自分が所有していない API を型消去で拡張する事例研究。JUCE のパラメータ階層(AudioProcessorParameter から Bool/Int/Float/Choice などが派生する複雑な木)を前に、そこへ共通操作を足したいが基底クラスには手を入れられない、という状況から出発する。
結論は型消去の適用条件を明確に述べる —あらかじめ型が決まっていない(あるいは型を制御できない)強く型付けされたオブジェクトの集合を管理し、その全要素に共通の操作を行いたいときに型消去は良い選択になる。
言い換えれば、継承もテンプレートも使わずに多相的な振る舞いを得るため、そして型とその操作を物理的に分離するために使う — それが結果としてサードパーティ製フレームワークの制約を越える手段になる。
Compile-Time Polymorphism for Runtime-Flexible Systems: Lessons from OpenJDK — Shubhankar Gambhir
「プロセス開始時に選ばれる選択(アロケータ、GC)」と「呼び出しを書く場所で選ばれる選択(relaxed か seq_cst か、幅はいくつか)」という2 つの軸が絡むと組み合わせ爆発する、という問題を OpenJDK の GC バリアで論じる。GC バリアはメモリバリアではなくコレクタのための簿記であり、コレクタごとに中身が違う。
実行時コストは解決後の間接呼び出し 1 回で関数ポインタと同等だが、テンプレートの実体化は実在するコストであり、午前 3 時にデバッガでパイプラインをステップ実行する代償は JEP に書かれない。
盗むべき 4 手順は、コンパイル時に既知のものを特定し、変化する軸を分離し(実装の選択は本当に実行時だが意味論はそうではない)、コンパイル時の軸を型システムへ移し、そして単純化する(定数条件によって通らない経路がデッドコードになり、生成コードはむしろ小さくなる)。ただし「呼び出し箇所を間違えると高くつく場合に限る。そうでなければ関数ポインタでほぼ足りる」という限定も置く。
MrDocs: Reliable Reference Documentation for C++ Using the Clang AST — Alan de Freitas
「関数について一番よく知っているのは、そのシグネチャを書いたまさにその瞬間である」という一文を出発点にする。生成と手書きのドキュメントを、書きやすさ・読みやすさ・AI フレンドリーさ・IDE 統合・検証可能性・ソースとの同期という軸で比較する。
Boost.Beast や Boost.URL は #ifdef BOOST_DOXYGEN でドキュメント用の偽の宣言を書かざるを得ない。そこで「コンパイラをそのまま使えばいいのでは」という発想から、Clang の AST を読む MrDocs が生まれる(既存の Doxygen → XML → docca(XSLT) → Quickbook → BoostBook → HTML というパイプラインを置き換える)。
シンボルコーパスは構造化データとして取り出せるため、ジェネレータはドキュメント生成に限られない。実例として 2 つのバージョンのコーパスを突き合わせ、追加・削除・変更(noexcept の差異など)を列挙して推奨されるバージョン番号の上げ方(major/minor)を出力する破壊的変更レポートのジェネレータを見せる。
API Design is Language Design: Lessons from Go's net/http library for C++ — Tim van Deurzen
「API 設計は言語設計である」を、SICP・認知科学・Go の net/http の 3 方向から支える発表。SICP の「工学的設計とは言語の層の連なりを作ることだ」を軸に据え、Felienne Hermans『The Programmer's Brain』から混乱の 3 分類(知識の不足・情報の不足・処理能力の不足)を借りる。
SICP の「プログラムは人が読むために書かれるべき」を「人と LLMが読むために」と言い換えるのが本発表の立場。Go の Handler はメソッド 1 つなので「正しい形をした任意の関数が Handler になる」(Rob Pike「インタフェースが大きいほど抽象は弱い」)。
C++ 版をコンセプトで書き起こしつつ、middleware の参照キャプチャのバグ(Go には GC とエスケープ解析がある)や、アプリのエラーを HTTP ステータスへ翻訳する「欠けている層」を指摘する。最後にテーブル駆動テストを「テストの言語」として扱う。
【7. ゲーム・グラフィックス・エミュレーション】
Inside a Small Game Engine: An Architecture Tour in Modern C++ — Elias Farhan
自作ゲーム『Soup Raiders』を独自の C++ エンジンへ移植し、Nintendo Switch 上で 60Hz・ロード 3 秒未満で動かすという目標を立て、それを達成するまでの記録。Steam では独自エンジンの比率が 2012 年の 71% から 2024 年には 13%(売上本数では 74% から 42%)まで下がっており、その少数派で何が必要かを示す。
構成はコンテナ、インデックスによるポインタ、シーン抽象、レンダリング、アセット読み込み、シリアライズの 6 つ。世代付きインデックス(generational index)を使うと、解放済みスロットへのアクセスが未定義動作ではなく検査の失敗として現れる。シーンでは dirty flag、レンダリングでは背面カリングと視錐台カリングが効果を出したが、いずれも Tracy による計測が先にあった。
C++26 の静的リフレクションはシリアライズのフィールド定義の重複をなくすが、第三者ライブラリの事情までは解決しない(glm::vec3 は無名共用体 3 つでできているため、汎用のウォーカーは cereal が 3 個の float を出す場面で 9 個を出してしまう)。締めは Tyler Glaiel の言葉で、エンジンはゲームなしには何物でもない — エンジンと並行してゲームを作ることが唯一にして破れない規則だという。
In Pursuit of a 6,000 FPS Game Boy Emulator — Tom Tesch
Game Boy エミュレータの 100 倍高速化の記録(実際には 5,973 FPS だったと冒頭で謝罪から入る)。実機 DMG-001 は 4,194,304 サイクル/秒 ÷ 70,224 サイクル/フレーム = 59.7275 FPS。
Sharp LR35902(SM83、Z80 と Intel 8080 を混ぜた設計で、完全にメモリマップド)の構成と、T-cycle(最高精度)と M-cycle(1 M-cycle = 4 T-cycle)の違いというエミュレーションの基礎から入り、Tetris を題材に最適化を積み上げていく。
なお終盤には、難読化して 1 画面に詰め込んだ Game Boy エミュレータのソースが登場する。
Writing High Performance Parsers Using State Machines — Torben Thaysen
Torbenx/charge のレキサ・パーサを題材に、状態機械による高性能パーサを解説する。Carbon が掲げた「レキシングとパースを 1,000 万行/秒」という目標に対し、Apple M4 上でレキシング 1,200 万行/秒、パース+レキシング 700 万行/秒を達成した実装が下敷き。
メインループの switch、+ 系トークン(++ += +)の分岐、文字列リテラル、/ とコメントの扱い、識別子テーブルを持つか否か(コンパイラには有用だがハッシュテーブル依存が重く、フォーマッタならキーワード認識で足りる)といった設計判断を積み上げる。
設計上の分岐点は「パース状態ごとにトークン認識を分ける」という選択で、単一の高速レキサより遅くなるが予測可能性が上がり、より多くを扱える。構文検証とフォーマッタには最適で、パースも設計次第で良い、という結論。計測手法も丁寧で、perf stat --control と FIFO を使って反復回数ごとの分岐ミス率を測る仕掛けまで公開する。
【8. テスト・コード品質・保守】
Ensuring Code Quality in the Age of AI: More Code, Less Engineering! — Peter Muldoon
「AI は大きな機会だが、コード生産を加速しながらそれを検証・保守するエンジニアリングシステムを強化しないと問題が起きる」という立場。
問題の核心はこう置かれる。LLM は次の単語を確率で推測する仕組みであり、知性は創発的で振る舞いは非決定的である。つまり「同じ入力は同じ出力を生み、一度テストすればその後は信頼できる」という従来型エンジニアリングの根本前提が崩れる。AI が自然に向くのはブレインストーミング、高速プロトタイピング、実験、ジャストインタイムの技術調査。
対策は組織の仕組みに落とす — レビューでは「なぜ承認したか」を誰にでも分かる形で残し、テストの存在ではなくテストが何の挙動を証明しているかを見る。リポジトリ側では自己承認の禁止、CI、静的解析、clang-tidy、重複検出、churn 検出。そして成果を測る(マージまでの時間、放棄された PR、無操作の承認、PR サイズ分布、本番障害と復旧時間)。行動喚起は「肉の代理人(meat proxy)になるな」— 読まず理解せず検証せずに AI の出力を転送する人のことで、自分の名前と評判を賭ける判断なら、AI にその根拠を説明させよ、と説く。
Familiar C++ Patterns That Fail at Scale and the Design Shifts That Prevent Them — Divya Chandrasekar, Devpriya Dave
Bloomberg の長寿システムが 3 度にわたって自分自身を追い越してしまった実話。書かれた当時の前提がスケールとともに崩れた経緯を、main() が返った後にオブジェクトがランタイムへコールバックしてクラッシュするスタックトレースなどで示す。
設計の変更は 3 つ —所有権を明示せよ(読むだけなら所有するな。所有が移るなら移動を可視化せよ)、依存を可視化せよ(ユニットテストに実システムが必要なら依存が隠れている)、不正な状態を表現不能にせよ(呼び出し側が規則を覚えていなければならないなら、インタフェースに符号化せよ)。
最後にエージェント時代への接続がある。AI はこれらの多くを指摘も修正もできるが、意図された契約(誰が所有すべきか、どの状態が本当に妥当か)は既存コードから推測するしかない。だから設計に明示的に符号化するほど、人間もエージェントも推測せずに済む。
Refactoring Techniques and Strategies (a Tale in Three Acts) — Dave Steffen
Martin Fowler の定義(観測可能な振る舞いを変えずに、理解しやすく変更コストを下げるための内部構造の変更)を確認したうえで進む 4 幕構成(基礎 / 問題 / 作業 / 説得)。
Code Smells を挙げつつ「コードスメルはバグではなく、何かおかしいかもしれないというヒントにすぎない」という限定を置く。目標はバグ修正でも最適化でもなく保守性であり、根底にあるのは単純化・単一責任・抽象度の向上。実務面では移行経路の設計(新 → 旧に転送、オーバーロード集合、[[deprecated]]、最後に削除して「悲鳴を聞き、負傷者を助ける」)を示す。
第 4 幕「売り込み」が本発表の特色で、「何も変わらない作業をなぜやるのか」への答えを用意する。再枠組みとして「プログラムは速くならないが、チームは速くなる」。そして測れないコード品質の代わりに、相関の高いもの(テストカバレッジ=テスト容易性、警告数、循環的複雑度、関数長)を測れ、と説く。
Code You Didn't Write: Understanding Large and Unfamiliar Codebases — Mike Shah, Chris Croft-White
初日に 100 万行超・20 年物のリポジトリを渡される — 一部はレガシー C++、一部は最新の C++26、そして AI が書いたために誰も完全には理解していない部分が増えつつある。そんな状況で使う技法とツールを、素朴な grep からデバッガ、プロファイラ、タイムトラベルデバッギング、AI 支援の探索まで紹介する。
情報源の信頼度をどこに置くかが全体を貫く — 意図は脆い真実の源であり、振る舞いこそが耐久性のある真実の源である。コメントもドキュメントも設計意図も嘘をつきうるが、実際に走らせて観察した挙動は検証できる。
締めは Douglas Adams の引用 —「何かを本当に理解したければ、誰かに説明してみるのが一番だ。生徒が鈍いほど、より単純な段階へ分解しなければならない。それこそがプログラミングの本質だ。教師は生徒より多くを学ぶ」。
Mock Any Function in C++ Without Changing a Single Line of Code — Ruifeng Zhang, Brian Rudo-Hutt, Ilya Ermakov
Bloomberg によるオープンソースの BonoboMock の紹介。モンキーパッチングにより、自由関数・静的関数・private メンバ関数までを、プロダクションコードを 1 行も変えずに gMock のモックにする。
DB::lookup() を直接呼ぶ 3 行の関数をテスト可能にするために、従来は依存性注入のためのリファクタリング(22 行の追加、呼び出し側は数えず)が必要だった。実装は関数先頭を数バイトのジャンプで書き換える方式で、Linux と Windows(ラムダを除く)に対応する。
実績は社内の約 1,000 プロジェクト。ただし適用範囲には限定を置く —「テストフレームワークが設計パターンを強制しなくなる分、保守したいコードを書く責任は自分に戻る。できるからといってやってよいとは限らない(たとえば private 関数のモック)」。
Extending Google Test for Statistical Benchmarking, CUDA Profiling, and CI Performance Regression — Kevin Gomez
「C++ の性能作業はテストスイートの外で生きている」という問題提起 —プロファイラごとに別ワークフロー、GPU にはさらに別の道具、そしてCI はユニットテストを全部走らせてベンチマークは 1 つも走らせないので、遅くなっても何も失敗せず、退行を見つけるのはユーザーになる。
Vernier はこれを Google Test の中に畳み込む。性能テストはただの Google Test のテストでよく、結果がテスト内の値なので時間予算超過やノイズ過多でビルドを落とせる。14 種のプロファイラがフラグ 1 つで同じテストバイナリに走り、「測る → プロファイラに聞く → 指された 1 箇所を直す → また測る」のループが文字列連結で 38 倍、実アプリで 251 倍を生んだ。
そして入力サイズを掃引せよ、1 点を信じるな — 最速の CPU 実装は N に依存して入れ替わり、CPU と GPU の優劣も N とバッチサイズ次第(N=50 では CPU が勝ち、バッチ 32 なら N=2190 で GPU が 7 倍)。
The journey to "/W4 /WX": How hard could it be? — Keith Stockdale
Rare Ltd(Sea of Thieves)による、巨大コードベースへの最大警告レベル + 警告のエラー化の導入記。規模は Sea of Thieves 29,936 ファイル / 325 万行、エンジン 19,580 ファイル / 347 万行、年間 27 万行以上の増加。
作業量は、/WX の有効化だけで 7,628 件の差分、C4189 の有効化で 254 件、レビュー対象の CL は 154 件。
結論は両論併記。警告は有用である — バグを見つけ、デッドコードを見つけ、不要な処理という性能問題を見つけ、しかもテスト・レビュー・QA・プレイヤーより遥か手前のコンパイル時に見つかるのでレビューも楽になる。ただし全部が割に合うわけではない — 警告の有用性はたいてい発火頻度に比例し、行数が増えるほど苦痛は増し、新しいコンパイラと標準が次々と新しい警告を持ち込む動く標的でもある。だからこそ「プロジェクトが進行してから警告レベルを上げるのは苦痛になる」(Jason Turner)。
AI is UB with Better PR — Andy Soffer, Matt Kulukundis
「AI は広報がうまい未定義動作である」という主張を 4 幕で展開する、AI とエージェント的開発の思考枠組み。UB の何が悪いのかを「任意に悪いことが起こりうる」「制御できない」「検出できない」の3 つが同時に成り立つことだと整理し(2 つまでなら扱える)、生成 AI も同じ三つ組を持つと論じる。
実例は Replit のエージェントによる本番 DB 削除、matplotlib のメンテナを調査してマージを迫るブログを無断公開した件など。対策は発生の制御(サンドボックス、yolo モードを使わない、静的解析)と検出(自動テスト、サニタイザ、監視)で、「ベストプラクティスは変わっていない。人間のエンジニアのために既に用意すべきものだ」と結ぶ。
第 2 幕では、同じ課題で Sonnet が自力で直そうとして誤ったのに対し Opus は = delete を付けてコンパイルエラーを収穫した例から「大きいモデルは自分で多くをこなすのではなく、ツールをうまく使う」を導き、信頼できるツールへ仕事を持ち上げよと説く。
【9. コンパイラ・言語処理系】
Escaping the AST: A Data-Oriented, Lock-Free Parallel Compiler Architecture — Aditya Kumar
Vx 言語のコンパイラフロントエンドにおけるデータ指向設計 — フラットな配列、256 ビットの識別子、競合すべきロックのないパイプライン。
モジュール以前の C++ では TU がパース・型検査・コード生成、そしてスケジューリングの単位であり、1 つの巨大 TU が実時間を決めてしまう。C++20 モジュールは再パースを終わらせる真の進歩だが、型依存がビルド順依存に変わり、長い import 連鎖がクリティカルパスになるという新しいコストを持ち込んだ。
Vx の答えは「中央のプールに尋ねない」こと — 型の識別子を内容から計算した 256 ビットのハッシュにすることで、Zig の InternPool のような共有プールもロックも要らなくなる(CI でロックゼロを強制している)。共有カウンタもロックだと見抜き、文字列の採番を prefix sum に置き換えた例がこの方針をよく表す。ただしこれは言語の側が譲ったから成り立つ(Zig の comptime は実行時に型を作るので名前をハッシュできない)という、言語とコンパイラの共同設計の議論として締めくくられる。
Type Punning: the joke is on you, pun intended. Fixing UB of reinterpret_cast! — Lieven de Cock
Andreas Fertig の記事と静的解析ツールの指摘がきっかけで始まった発表。「自分も知らなかった」「同僚も知らなかった」「専門家ですらキャリア後半で知った」という前置きが、この領域の誤解の広さを示す。
型システムを迂回して同じメモリを別の型として扱う type punning を定義し、2 つの変数の「回顧録」という語り口でオブジェクトのライフタイム(1 つの位置には 1 つのオブジェクトしか生きられない)とアラインメントを導入する。
結論は実務的な行動計画になっている — reinterpret_cast が UB になる理由は数多くあるので、最新の道具に追いつけ(C++20 の std::bit_cast と寿命開始関数、C++23 の std::start_lifetime_as)。grep しにくい C スタイルキャストは -Wold-style-cast で警告にして洗い出し、まず C++ の専用キャストへ置き換えよ。
【10. 数値計算・GPU】
CUDA Tile C++: Practical Automatic Parallelization for the GPU — Ezra Stein
GPU プログラマが負ってきた責務(ハードウェアスレッドへのデータ割り当て、競合の回避、データ移動とキャッシュの管理、リソースを 100% 使い切ること)をコンパイラに移す CUDA Tile C++ の紹介。プログラマは配列に対する SIMD 的な操作でシングルスレッドのコードを書き、コンパイラがハードウェアへスケジューリングする。
現状の行列積はインラインの PTX アセンブリ(wmma.load / wmma.mma.sync)や CUtensorMap を直に書く羽目になっている。Tile C++ ではタイルとテンソルスパン、パーティションビューで書け、反復空間が既知のループ・入力の非重複宣言・アラインメント宣言が自動並列化と TMA の利用を可能にする。
「Tile C++ は C++ である」— 汎用であり、クラス・共用体・大半の制御フロー・ポインタ演算をそのまま使える。同じファイル内で __global__(SIMT)と __tile_global__ を共存させられる。CUDA Toolkit 13.3 から利用可能(Ampere 以降、C++20 以上、nvcc --enable-tile)。
Scaling Robotics to 200 Developers: Flexible C++ Architectures for Kinematics, Dynamics, Simulation & Control — Carl Saldanha
Amazon の倉庫でピッキング・仕分けを行うマニピュレーションロボティクス部門による、ロボティクスアルゴリズムのインタフェース設計(最大のプロジェクトでは 100 億個以上の荷物)。焦点は計画(軌道、運動学/動力学、衝突判定)。
インタフェースの目標は 4 つ — 堅牢(インタフェースを決して変えない)、高性能、拡張可能(ライブラリの変化をテストして吸収できる)、モダンで読みやすい C++。自己評価では、制御インタフェースが最も拡張しにくく、性能劣化にも最も弱い。
まとめは「標準は 14 個ある。それでいい」— 抽象の釣り合いが妥当なら、それは良い標準である。短くまとめるなら、バックエンドを差し替え可能にし、計画・制御・シミュレーションで同じ型を使い、そしてアルゴリズムと型をできる限り分離せよ。
セキュリティ・メモリ安全性
Hacking and Securing C++ — Marcell Juhasz
組み込みデバイス(UART/USB 経由のプロトコル)を題材に、バグから脆弱性、脆弱性から攻撃、そして緩和策までを一通り辿る。
前提として組み込みのメモリモデルと、スタックの解放ではデータは消えないことを丁寧に整理したうえで、固定長バッファへの受信からスタックを破壊し、制御フローを乗っ取り、ROP ガジェットを連ねて execve("/bin/sh") に至るところまで実演する。
対策は 2 段構え。システム側では非実行スタック(MPU)、ASLR(そのためにアドレスをログに出さない)、スタックカナリア、そしてクライアント側の検証を決して信用しないこと。言語側の結論は「モダン C++ を使え」— 要素数を配列に結びつけ(範囲 for、std::array)、memcpy のような無制限なバッファ操作をカプセル化し、動的確保したオブジェクトは必ず初期化する。
その他
The Clocks of C++: Knowing When (and Why) to Use Each One — Sandor Dargo
計装プロジェクトでタイムスタンプ・クロック型・期間の変換を扱った経験から生まれた発表。C 時代の時間 API の問題(型安全性の欠如、単位の混同、合成不能)を確認し、<chrono> の 3 本柱を解説する。
C++20 のタイムゾーン対応まで進み、zoned_time が時刻と場所を束ねることで夏時間の巻き戻しで曖昧になる時刻や、春先に飛ばされる時刻をライブラリが正しく扱えるようになる実利を示す。
結論は「時間は単なる数ではない」という発想の転換。単調時間か暦時間か、値は飛びうるか、測りたいのか印字したいのかを毎回問え。用途別の早見表(経過時間は steady_clock、暦の論理は system_clock + time_zone)と、system_clock::now() はモックできないので包んで注入せよ — 時間は依存関係として扱えという設計指針で締める。
Pointers to Power: Navigating Organizational Influence — Matt Kulukundis
SwissTable の作者が自身のキャリアを辿りながら「組織の中でどう影響力を持つか」を語る発表。base への変更を Sanjay に即座に「No」と言われた経験から、相手が何を大事にしているかを理解してから、相手が聞く用意のある議論を提示することを学ぶ。
意見が割れた社内ライブラリの調停で編み出したのが文書による意思決定の型 —選択肢(「何もしない」を必ず含める)と長所短所を網羅し、関係者それぞれに自分の名前で段落を書かせる。
規則は「意見は重み付けであって、事実を持ち込んではいけない」。会議ではなく文書にするのは、会議では立場がその場で硬直するから。SwissTable では反対意見を「賛成するために何が必要かの説明」と捉え、影響を単一の数字ではなく悲観・楽観の幅で示して信用を得た。
一方、Noogler 向け提案が実績を積んでもなお通らなかった失敗も語られる。
【11. Lightning Talks】
operator<> — Ben Deane
「高階メタプログラミングの平等を求める夢」という副題のライトニングトーク。現在のメタプログラミングは 20 年前の高階関数と同じ場所にいると指摘する —テンプレートは型ではなく、型とテンプレートを一様に渡せず、テンプレートを返せず、部分適用も合成もできない。
20 年前の答えはライブラリ(Boost.Bind、Boost.Lambda)で、それでは足りないと分かって C++11 でラムダ式を言語に入れた。同じことをメタ世界でやろう、というのが operator<> の提案で、メタ値とメタ関数を一様に渡せ、メタ関数を返せ、合成と部分適用が単純になる。
「リフレクションがあるのでは」への答えが、この提案の位置づけを決めている —リフレクションは言語が許すものしか反映できない。だからテンプレートメタプログラミングを補完するのであって、置き換えはしない。易しくはするが、単純にはしない。
Much Ado About Null — Keith Stockdale
TestNotNullOrAbort というヌルチェックヘルパの設計を段階的に改善していくライトニングトーク。
メッセージ付きとなしの 2 つのオーバーロードがあると、ポインタを渡すべき場所に文字列を渡すコードが通ってしまう。これを concepts(requires (!TIsCharTypeV<PointerType>))で塞ぐ。だがチェックを通った後も「このポインタは本当にまだ非 null か」という疑問は残る。auto* const で再代入を防ぐ案は複雑さを持ち込むので退け、参照を返す設計に到達する。
実装は難しくなるが、static_assert 群で戻り値型(int32* → int32&、void* → void)を検証でき、その帰結としてnullptr を渡すコードがコンパイルエラーになる。
Eight Bytes From a Six-Byte Field — Mohammad Al-Hanoosh
6 バイトのアドレスを比較するのに、8 バイト幅で読むのと 4+2 バイトに分けて読むのとどちらが速いかを測ったライトニングトーク。8 バイト版は ((x ^ y) << 16) == 0 で余分な 2 バイトを落とし、分割版は ((x ^ y) | (u ^ v)) == 0 でちょうど 6 バイトしか読まない。どちらも分岐を持たない。
結果は 8 バイト版 0.35ns/ペア、分割版 0.81ns/ペア、memcmp 6 バイト 0.58ns/ペア。理由はワイドループでは 2 つの 16 バイトロードが 2 ペア分をまとめて取り込み、punpckhqdq/punpcklqdq でグループ化できるのに対し、分割版は小片を集めるためにより多くのロードと unpack を必要とする(8 ペアあたりの命令数で比較して示される)。
結論は一行 —「読むバイト数を減らしても、この比較は速くならなかった」。
![[改訂第5版]C++ポケットリファレンス (Pocket reference) [改訂第5版]C++ポケットリファレンス (Pocket reference)](https://m.media-amazon.com/images/I/41aRUjBsebL._SL500_.jpg)