<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Cpp on Wuyukwi's Notebook</title><link>https://wuyukwi.duckdns.org/tags/cpp/</link><description>Recent content in Cpp on Wuyukwi's Notebook</description><generator>Hugo</generator><language>ja</language><lastBuildDate>Wed, 11 Mar 2026 19:31:51 +0900</lastBuildDate><atom:link href="https://wuyukwi.duckdns.org/tags/cpp/index.xml" rel="self" type="application/rss+xml"/><item><title>🙌 なぜ Unity は C# を選び、Unreal は選ばなかったのかについての個人の考察</title><link>https://wuyukwi.duckdns.org/posts/zenn-97b70df23bd792/</link><pubDate>Wed, 11 Mar 2026 19:31:51 +0900</pubDate><guid>https://wuyukwi.duckdns.org/posts/zenn-97b70df23bd792/</guid><description>&lt;blockquote&gt;
&lt;p&gt;📌 &lt;em&gt;この記事は &lt;a href="https://zenn.dev/wuyukwi/articles/97b70df23bd792"&gt;Zenn&lt;/a&gt; に投稿した内容のアーカイブです。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id="はじめに"&gt;はじめに&lt;/h1&gt;
&lt;p&gt;ゲームエンジンの言語の話になると、よくこんな対比が出てきます。&lt;/p&gt;
&lt;p&gt;Unity は C#。&lt;br&gt;
Unreal は C++ と Blueprint。&lt;/p&gt;
&lt;p&gt;この違い、ただの好みではありません。&lt;br&gt;
エンジンの設計思想そのものに関わっています。&lt;/p&gt;
&lt;p&gt;C# が優れているとか、C++ が古いとか、そういう単純な話ではない。&lt;br&gt;
両者はそもそも目指している開発体験が違うのです。&lt;/p&gt;
&lt;hr&gt;
&lt;h1 id="unity-が辿った言語統一の歴史"&gt;Unity が辿った言語統一の歴史&lt;/h1&gt;
&lt;p&gt;今でこそ「Unity といえば C#」という印象がありますが、最初からそうだったわけではありません。&lt;br&gt;
初期の Unity では、開発者のバックグラウンドに合わせて複数の言語を選択できるのが特徴でした。&lt;/p&gt;
&lt;p&gt;具体的には次の3つです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;C#&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;UnityScript&lt;/strong&gt; （JavaScript 風の独自言語）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Boo&lt;/strong&gt; （Python 風の言語）&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当時の Unity は「スクリプト言語の自由度」を売りの一つにしており、Web 開発者は UnityScript、Python 系に慣れている人は Boo、という具合に選べるようになっていました。&lt;/p&gt;
&lt;p&gt;しかし、この方針は長くは続きませんでした。&lt;/p&gt;
&lt;p&gt;Unity 2017 頃から段階的に非推奨となり、&lt;br&gt;
&lt;strong&gt;Unity 2019.1 を最後に UnityScript と Boo は完全に削除&lt;/strong&gt; されます。&lt;/p&gt;
&lt;p&gt;結果として、Unity のスクリプト言語は &lt;strong&gt;C# のみ&lt;/strong&gt; に統一されました。&lt;/p&gt;
&lt;p&gt;参考&lt;br&gt;
&lt;a href="https://discussions.unity.com/t/which-release-removed-support-unityscript-and-boo/1624299"&gt;https://discussions.unity.com/t/which-release-removed-support-unityscript-and-boo/1624299&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;なぜこうなったのか。理由はいくつかあります。&lt;/p&gt;
&lt;p&gt;まず、複数言語の維持コストです。&lt;br&gt;
エンジン側はコンパイラ、ツールチェーン、デバッグ環境などをすべてサポートし続ける必要があります。&lt;/p&gt;
&lt;p&gt;さらに重要なのは、UnityScript と Boo の実態です。&lt;br&gt;
これらは見た目こそ別言語でしたが、内部では Mono 上で動作するスクリプトでした。&lt;/p&gt;</description></item><item><title>🎉 Git を「完全に理解」するためのデータ構造の話</title><link>https://wuyukwi.duckdns.org/posts/zenn-12736c19b679d6/</link><pubDate>Wed, 25 Feb 2026 17:25:36 +0900</pubDate><guid>https://wuyukwi.duckdns.org/posts/zenn-12736c19b679d6/</guid><description>&lt;blockquote&gt;
&lt;p&gt;📌 &lt;em&gt;この記事は &lt;a href="https://zenn.dev/wuyukwi/articles/12736c19b679d6"&gt;Zenn&lt;/a&gt; に投稿した内容のアーカイブです。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id="はじめに"&gt;はじめに&lt;/h1&gt;
&lt;p&gt;Git を使っていて、こんな感覚になったことはありませんか？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;コマンドは打てるけど、何が起きているか分からない&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;merge と rebase の違いを、説明しろと言われると詰まる&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;conflict が出ると、なぜ衝突したのか理解できない&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;多くの Git 解説記事は、&lt;br&gt;
&lt;strong&gt;「何を打つか」&lt;/strong&gt; は丁寧に教えてくれますが、&lt;br&gt;
&lt;strong&gt;「なぜそうなるか」&lt;/strong&gt; までは踏み込んでくれません。&lt;/p&gt;
&lt;p&gt;その結果、&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;なんとなく使えているが、仕組みはよく分からない&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;という状態に陥りがちです。&lt;/p&gt;
&lt;p&gt;この記事では、コマンドの説明はほぼ行いません。&lt;br&gt;
代わりに、&lt;strong&gt;Git の内部構造をデータ構造として捉え直す&lt;/strong&gt; ことで、&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;「あ、そういうことだったのか」&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;と腑に落ちる理解を目指します。&lt;/p&gt;
&lt;p&gt;少しだけ &lt;strong&gt;グラフ理論&lt;/strong&gt; と &lt;strong&gt;ポインタの概念（C 言語）&lt;/strong&gt; の素養があると、&lt;br&gt;
かなりスムーズに理解できると思います。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="git-の正体木構造ではなく-dag有向非巡回グラフ"&gt;Git の正体：木構造ではなく DAG（有向非巡回グラフ）&lt;/h2&gt;
&lt;p&gt;多くの人は、Git の履歴を「木構造」としてイメージしています。&lt;/p&gt;
&lt;p&gt;しかし、これは正確ではありません。&lt;/p&gt;
&lt;p&gt;Git の本質は &lt;a href="https://ja.wikipedia.org/wiki/%E6%9C%89%E5%90%91%E9%9D%9E%E5%B7%A1%E5%9B%9E%E3%82%B0%E3%83%A9%E3%83%95"&gt;有向非巡回グラフ&lt;/a&gt;です。&lt;/p&gt;
&lt;h3 id="commit-はノード"&gt;Commit は「ノード」&lt;/h3&gt;
&lt;p&gt;すべての commit は、グラフ上の &lt;strong&gt;ノード&lt;/strong&gt; です。&lt;/p&gt;
&lt;p&gt;各 commit は、&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;ファイルのスナップショット&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;親 commit への参照（1 個または 2 個）&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;を持っています。&lt;/p&gt;
&lt;h3 id="親が-2-個になる瞬間--merge"&gt;親が 2 個になる瞬間 = merge&lt;/h3&gt;
&lt;p&gt;通常の commit は親が 1 つですが、&lt;br&gt;
merge commit は &lt;strong&gt;親を 2 つ&lt;/strong&gt; 持ちます。&lt;/p&gt;</description></item><item><title>😎 「リンクリストはもう使われない」のか？</title><link>https://wuyukwi.duckdns.org/posts/zenn-f01b1a0f7f2884/</link><pubDate>Mon, 02 Feb 2026 15:20:02 +0900</pubDate><guid>https://wuyukwi.duckdns.org/posts/zenn-f01b1a0f7f2884/</guid><description>&lt;blockquote&gt;
&lt;p&gt;📌 &lt;em&gt;この記事は &lt;a href="https://zenn.dev/wuyukwi/articles/f01b1a0f7f2884"&gt;Zenn&lt;/a&gt; に投稿した内容のアーカイブです。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="2026年のハードウェア前提で考えるデータ構造の現実"&gt;――2026年のハードウェア前提で考えるデータ構造の現実&lt;/h2&gt;
&lt;p&gt;エンジニア向けの記事や SNS で、&lt;br&gt;
「リンクリストはもう終わった」&lt;br&gt;
という表現を見かけることがあります。&lt;/p&gt;
&lt;p&gt;最初にこの話を聞いたとき、正直かなり極端だなと思いました。&lt;br&gt;
ただ、実務でパフォーマンスを意識した設計や実装を重ねていくと、この言い方が&lt;strong&gt;完全に間違っているとも言い切れない&lt;/strong&gt; 、という感覚になってきます。&lt;/p&gt;
&lt;p&gt;この記事では、「リンクリストがなぜ避けられるようになったのか」「それでも今なお使われ続けている理由」を、&lt;strong&gt;現代の CPU・メモリ構成を前提に&lt;/strong&gt; 整理してみます。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="教科書的な理解と現実のズレ"&gt;教科書的な理解と、現実のズレ&lt;/h2&gt;
&lt;p&gt;学生時代にデータ構造を学んだとき、ほとんどの人が次のように習ったはずです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;配列：途中への挿入・削除は O(N)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;リンクリスト：挿入・削除は O(1)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;理論としては正しいです。&lt;br&gt;
ただし、ここには&lt;strong&gt;重要な前提条件&lt;/strong&gt; があります。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;メモリアクセスのコストはすべて同じである&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;現実のハードウェアでは、これは成立しません。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="キャッシュ階層という無視できない現実"&gt;キャッシュ階層という「無視できない現実」&lt;/h2&gt;
&lt;p&gt;現代の CPU では、メモリは階層構造になっています。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;L1 キャッシュ：数サイクル&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;L2 キャッシュ：十数サイクル&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;L3 キャッシュ：数十サイクル&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;メインメモリ（RAM）：数百サイクル&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;この差は無視できるものではありません。&lt;br&gt;
特に問題になるのが、&lt;strong&gt;アクセスパターンが予測できない場合&lt;/strong&gt; です。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="リンクリストが不利になる理由"&gt;リンクリストが不利になる理由&lt;/h2&gt;
&lt;p&gt;典型的なリンクリストは、ノードがメモリ上に散らばって配置されます。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Node A -&amp;gt; Node B -&amp;gt; Node C&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;見た目はシンプルですが、実際のアドレスは次のようになっているかもしれません。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;0x1000 -&amp;gt; 0x8F20 -&amp;gt; 0x3A10&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;リンクリストを走査する場合、&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;現在のノードを読み込む&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;next ポインタを読む&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;次のアドレスが分かってから、次の読み込みを行う&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;という&lt;strong&gt;強い依存関係のある処理&lt;/strong&gt; になります。&lt;/p&gt;
&lt;p&gt;CPU のプリフェッチ機構は、連続したアクセス（配列など）は得意ですが、&lt;br&gt;
リンクリストのようなポインタ追跡はほとんど先読みできません。&lt;/p&gt;
&lt;p&gt;結果として、&lt;strong&gt;キャッシュミスが頻発&lt;/strong&gt; します。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="配列vectorが速い本当の理由"&gt;配列（vector）が速い本当の理由&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;std::vector&lt;/code&gt; や配列を順番に処理しているとき、&lt;br&gt;
実際には CPU はすでに次のデータをキャッシュに読み込んでいます。&lt;/p&gt;</description></item><item><title>🦔 Overwatchにおけるネットコードと予測技術の解説</title><link>https://wuyukwi.duckdns.org/posts/zenn-ee81f79477d4f9/</link><pubDate>Fri, 21 Nov 2025 13:06:29 +0900</pubDate><guid>https://wuyukwi.duckdns.org/posts/zenn-ee81f79477d4f9/</guid><description>&lt;blockquote&gt;
&lt;p&gt;📌 &lt;em&gt;この記事は &lt;a href="https://zenn.dev/wuyukwi/articles/ee81f79477d4f9"&gt;Zenn&lt;/a&gt; に投稿した内容のアーカイブです。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="はじめに高速対戦ゲームにおけるネットコードの本質"&gt;はじめに：高速対戦ゲームにおけるネットコードの本質&lt;/h2&gt;
&lt;p&gt;ゲームプレイ（GamePlay）エンジニアにとって、最重要テーマのひとつが &lt;strong&gt;ネット同期（netcode）&lt;/strong&gt; です。&lt;br&gt;
特に Overwatch のような高速リアルタイムアクションでは、&lt;strong&gt;「レスポンスの速さ（responsive）」&lt;/strong&gt; がゲーム性を決定づけます。&lt;/p&gt;
&lt;p&gt;そのため、プレイヤーの操作は &lt;strong&gt;サーバーの応答を待たずに即時反映&lt;/strong&gt; する必要があります。&lt;br&gt;
これを実現するのが &lt;strong&gt;予測（prediction / pre-presentation）&lt;/strong&gt; です。&lt;/p&gt;
&lt;p&gt;ただし、クライアントを信頼できない（チート対策）というFPSの前提は20年変わっていません。&lt;br&gt;
そのうえで「即時応答」と「サーバー権威」の両立を図るのがネットコード設計の核心になります。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="即時応答が必要な操作"&gt;即時応答が必要な操作&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;移動&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;スキル&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;武器（発射や装填などのスキル付き武器）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;当たり判定（Hit Registration）&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;共通原則はただ一つ：&lt;br&gt;
&lt;strong&gt;「プレイヤーがボタンを押したら即、見た目の挙動が起きること」&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;どれほど ping が高くても、遅延を感じさせてはならない。&lt;/p&gt;
&lt;p&gt;しかし、予測を行う以上、避けられない問題があります──&lt;br&gt;
それが &lt;strong&gt;予測ミス（misprediction）&lt;/strong&gt; です。&lt;/p&gt;
&lt;p&gt;予測ミスは、&lt;br&gt;
&lt;strong&gt;「クライアント側では成功したように見えた操作が、サーバー上では成立していない」&lt;/strong&gt;&lt;br&gt;
状態を指します。&lt;/p&gt;
&lt;p&gt;Overwatch では、予測ミスが起きても「操作を遅延させる」のではなく、&lt;br&gt;
&lt;strong&gt;できる限り予測ミスを減らす設計（＝確定性 Determinism）&lt;/strong&gt; が採用されています。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="予測ミスの例サーバーとの食い違い"&gt;予測ミスの例：サーバーとの食い違い&lt;/h2&gt;
&lt;p&gt;ping 250ms。&lt;br&gt;
クライアントでは「ジャンプした」つもりでも、サーバー上では &lt;strong&gt;Mei の氷結を受けていた&lt;/strong&gt; 。&lt;br&gt;
その結果、クライアントは一度ジャンプの予測を行った後、サーバーからの正しい状態（氷結）で「強制的に巻き戻される」。&lt;/p&gt;
&lt;p&gt;このように、超高速応答を目指すほど、予測ミスが発生しやすくなります。&lt;/p&gt;
&lt;hr&gt;
&lt;h1 id="-ここから低ミス予測を支える確定性determinism"&gt;▼ ここから：低ミス予測を支える「確定性（Determinism）」&lt;/h1&gt;
&lt;p&gt;以下では Overwatch が採用している &lt;strong&gt;ECS + Deterministic Simulation&lt;/strong&gt; の要点を整理します。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="1-時間の量子化command-frame"&gt;1. 時間の量子化：Command Frame&lt;/h2&gt;
&lt;p&gt;確定的なシミュレーションは、&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;時間の同期&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;固定更新周期&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;値の量子化&lt;/p&gt;</description></item><item><title>🌟 C++11 が左辺値と右辺値を5分類に拡張した理由</title><link>https://wuyukwi.duckdns.org/posts/zenn-bdadcd1b03c135/</link><pubDate>Mon, 27 Oct 2025 14:31:00 +0900</pubDate><guid>https://wuyukwi.duckdns.org/posts/zenn-bdadcd1b03c135/</guid><description>&lt;blockquote&gt;
&lt;p&gt;📌 &lt;em&gt;この記事は &lt;a href="https://zenn.dev/wuyukwi/articles/bdadcd1b03c135"&gt;Zenn&lt;/a&gt; に投稿した内容のアーカイブです。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="はじめに"&gt;はじめに&lt;/h2&gt;
&lt;p&gt;C++11 で「右辺値参照（rvalue reference）」が導入されたことで、C++ の値分類（value category）は大きく進化しました。&lt;br&gt;
C++98 では「左辺値（lvalue）」と「右辺値（rvalue）」の二分法しかありませんでしたが、C++11 ではさらに細かく分類されます：&lt;/p&gt;
&lt;p&gt;C++11で導入された新しい概念である&lt;br&gt;
&lt;strong&gt;xvalue（期限切れ右辺値）&lt;/strong&gt; と &lt;strong&gt;prvalue（純粋右辺値）&lt;/strong&gt; が、&lt;br&gt;
この章の中核的な進化ポイントです。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="c98-時代の単純な世界"&gt;C++98 時代の「単純な世界」&lt;/h2&gt;
&lt;p&gt;C++98 の時代はとてもシンプルでした。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;左辺値 (lvalue)&lt;/strong&gt; ：代入の左側に置けるもの。永続的なアドレスを持つ。&lt;br&gt;
例：&lt;code&gt;a&lt;/code&gt;, &lt;code&gt;*ptr&lt;/code&gt;, &lt;code&gt;array[i]&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;右辺値 (rvalue)&lt;/strong&gt; ：一時的な値。すぐに破棄される。&lt;br&gt;
例：&lt;code&gt;a + 5&lt;/code&gt;, &lt;code&gt;42&lt;/code&gt;, &lt;code&gt;func()&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ルールも直感的でした：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int a = 3;
&amp;amp;a; // OK 左辺値はアドレスが取れる
&amp;amp;(a + 5); // エラー！右辺値はアドレスが取れない
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;しかしこの単純さが、&lt;strong&gt;性能の壁&lt;/strong&gt; になっていました。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="問題無駄なコピーが多すぎる"&gt;問題：無駄なコピーが多すぎる&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;std::vector&amp;lt;int&amp;gt; create_vector() {
std::vector&amp;lt;int&amp;gt; temp;
temp.push_back(1);
temp.push_back(2);
temp.push_back(3);
return temp; // ここで何が起こる？
}
std::vector&amp;lt;int&amp;gt; v = create_vector();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;C++98 の仕様では &lt;code&gt;return temp;&lt;/code&gt; で &lt;strong&gt;コピーが発生&lt;/strong&gt; します。&lt;br&gt;
&lt;code&gt;temp&lt;/code&gt; のヒープメモリをまるごと複製し、呼び出し側に返すのです。&lt;/p&gt;</description></item><item><title>💡 C++ の「奇妙な再帰テンプレートパターン（CRTP）」を理解する</title><link>https://wuyukwi.duckdns.org/posts/zenn-b13f0c8ee91a17/</link><pubDate>Tue, 30 Sep 2025 12:36:20 +0900</pubDate><guid>https://wuyukwi.duckdns.org/posts/zenn-b13f0c8ee91a17/</guid><description>&lt;blockquote&gt;
&lt;p&gt;📌 &lt;em&gt;この記事は &lt;a href="https://zenn.dev/wuyukwi/articles/b13f0c8ee91a17"&gt;Zenn&lt;/a&gt; に投稿した内容のアーカイブです。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="はじめに"&gt;はじめに&lt;/h2&gt;
&lt;p&gt;C++ のテンプレートには数多くの応用テクニックがありますが、その中でも「&lt;strong&gt;CRTP（Curiously Recurring Template Pattern：奇妙な再帰テンプレートパターン）&lt;/strong&gt; 」は特にユニークで強力な手法です。&lt;br&gt;
Boost をはじめとする有名ライブラリでも多用されており、C++ の表現力を大きく広げてくれます。&lt;/p&gt;
&lt;p&gt;この記事では、CRTP の基本から実用例、さらには応用パターンまでを整理して解説します。&lt;/p&gt;
&lt;h2 id="crtp-とは"&gt;CRTP とは？&lt;/h2&gt;
&lt;p&gt;CRTP の基本形は以下のようになります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;template &amp;lt;typename T&amp;gt;
class Base {
public:
T&amp;amp; getDerived() { return static_cast&amp;lt;T&amp;amp;&amp;gt;(*this); }
};
class Derived : public Base&amp;lt;Derived&amp;gt; {
// Derived 自身を Base のテンプレート引数として渡す
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一見すると「派生クラス &lt;code&gt;Derived&lt;/code&gt; が、まだ完成していない自分自身を基底クラス &lt;code&gt;Base&lt;/code&gt; に渡している」ように見えます。この“自己参照”こそが CRTP の特徴です。&lt;br&gt;
コンパイラはこれを問題なく処理でき、基底クラスはコンパイル時に派生クラスの型情報を知ることができます。&lt;/p&gt;
&lt;h2 id="実用例比較演算子の自動生成"&gt;実用例：比較演算子の自動生成&lt;/h2&gt;
&lt;p&gt;C++ では &lt;code&gt;==&lt;/code&gt; や &lt;code&gt;&amp;lt;&lt;/code&gt; などの比較演算子を定義するのはよくある作業ですが、すべてを個別に書くと冗長になりがちです。&lt;br&gt;
CRTP を用いると、基底クラスに「共通の比較演算子」をまとめ、派生クラスは最小限の実装だけで済ませられます。&lt;/p&gt;
&lt;h3 id="基底クラス"&gt;基底クラス&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;template &amp;lt;typename Derived&amp;gt;
class Comparable {
public:
bool operator==(const Derived&amp;amp; other) const {
return static_cast&amp;lt;const Derived*&amp;gt;(this)-&amp;gt;equalsImpl(other);
}
bool operator!=(const Derived&amp;amp; other) const {
return !(*this == other);
}
bool operator&amp;lt;(const Derived&amp;amp; other) const {
return static_cast&amp;lt;const Derived*&amp;gt;(this)-&amp;gt;lessImpl(other);
}
bool operator&amp;gt;(const Derived&amp;amp; other) const { return other &amp;lt; *this; }
bool operator&amp;lt;=(const Derived&amp;amp; other) const { return !(*this &amp;gt; other); }
bool operator&amp;gt;=(const Derived&amp;amp; other) const { return !(*this &amp;lt; other); }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="派生クラス例"&gt;派生クラス例&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;class Point : public Comparable&amp;lt;Point&amp;gt; {
public:
Point(int x_val, int y_val) : x(x_val), y(y_val) {}
bool equalsImpl(const Point&amp;amp; other) const {
return x == other.x &amp;amp;&amp;amp; y == other.y;
}
bool lessImpl(const Point&amp;amp; other) const {
return (x != other.x) ? (x &amp;lt; other.x) : (y &amp;lt; other.y);
}
private:
int x, y;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これで &lt;code&gt;==&lt;/code&gt;, &lt;code&gt;!=&lt;/code&gt;, &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;=&lt;/code&gt;, &lt;code&gt;&amp;gt;=&lt;/code&gt; がすべて利用可能になります。&lt;/p&gt;</description></item><item><title>😯 C++で高性能計算を書くときに意識すべき最適化ポイントまとめ</title><link>https://wuyukwi.duckdns.org/posts/zenn-d2508bad444e4d/</link><pubDate>Mon, 04 Aug 2025 20:10:19 +0900</pubDate><guid>https://wuyukwi.duckdns.org/posts/zenn-d2508bad444e4d/</guid><description>&lt;blockquote&gt;
&lt;p&gt;📌 &lt;em&gt;この記事は &lt;a href="https://zenn.dev/wuyukwi/articles/d2508bad444e4d"&gt;Zenn&lt;/a&gt; に投稿した内容のアーカイブです。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="はじめに"&gt;はじめに&lt;/h2&gt;
&lt;p&gt;本記事では、私自身が経験したことや、信頼できる実践的な知見を元に、&lt;strong&gt;C++で高速な数値処理や行列演算を行う際に意識すべき最適化ポイント&lt;/strong&gt; をわかりやすく整理しました。&lt;br&gt;
HPCやリアルタイム演算に関心のある方に参考になれば幸いです。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="1-メモリのアライメントとキャッシュ効率"&gt;1. メモリのアライメントとキャッシュ効率&lt;/h2&gt;
&lt;h3 id="アライメント__attribute__alignedn"&gt;アライメント（&lt;code&gt;__attribute__((aligned(n))&lt;/code&gt;）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;CPUは&lt;strong&gt;キャッシュライン（通常64バイト）単位&lt;/strong&gt; でメモリを読み込む。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;メモリが正しく整列されていないと、&lt;strong&gt;キャッシュミスやfalse sharing&lt;/strong&gt; が発生し、性能が大幅に低下。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;GCC/Clangなら &lt;code&gt;__attribute__((aligned(32)))&lt;/code&gt; など、MSVCでは &lt;code&gt;__declspec(align(32))&lt;/code&gt; を使う。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="連続したメモリを使用するstdvectortなど"&gt;連続したメモリを使用する（&lt;code&gt;std::vector&amp;lt;T&amp;gt;&lt;/code&gt;など）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;行列は行優先(row-major)の順番で連続配置することで、CPUの事前読み込み(prefetch)が効きやすくなる。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;std::vector&amp;lt;std::vector&amp;lt;float&amp;gt;&amp;gt;&lt;/code&gt; のような非連続構造は避ける。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="2-メモリ確保の最適化高速メモリアロケータ"&gt;2. メモリ確保の最適化：高速メモリアロケータ&lt;/h2&gt;
&lt;p&gt;標準の &lt;code&gt;malloc&lt;/code&gt; や &lt;code&gt;new&lt;/code&gt; は汎用的で安全ですが、性能面ではボトルネックになることがあります。&lt;/p&gt;
&lt;h3 id="おすすめのアロケータ"&gt;おすすめのアロケータ&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;tcmalloc&lt;/strong&gt; （Google製、スレッドセーフ）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;jemalloc&lt;/strong&gt; （Facebook製、ゲームやサーバーで人気）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Hoard&lt;/strong&gt; （スケーラブルなスレッド対応）&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これらを使うと、&lt;strong&gt;多スレッド環境でのアロケーション性能が大幅に向上&lt;/strong&gt; します。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="3-行列演算の高速化手法"&gt;3. 行列演算の高速化手法&lt;/h2&gt;
&lt;h3 id="strassenアルゴリズムon281"&gt;Strassenアルゴリズム（O(n^2.81)）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;通常のO(n³)よりも理論上速い。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;再帰的に分割しながら計算することで、掛け算回数を削減。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="ブロック分割block-based-multiplication"&gt;ブロック分割（Block-based Multiplication）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;大きな行列を&lt;strong&gt;小さなサブブロックに分けて計算&lt;/strong&gt; すると、キャッシュが有効活用できる。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;代表的なライブラリ（後述するBLASなど）ではこの最適化が組み込まれている。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="4-並列計算マルチスレッド化"&gt;4. 並列計算（マルチスレッド化）&lt;/h2&gt;
&lt;h3 id="c11の標準スレッド"&gt;C++11の標準スレッド&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;std::thread&lt;/code&gt;, &lt;code&gt;std::async&lt;/code&gt; を使って並列化可能。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;シンプルな制御だが、スレッド数の管理や同期は自前で対応が必要。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="openmp"&gt;OpenMP&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;#pragma omp parallel for&lt;/code&gt; のように、簡潔にfor文を並列化。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;コンパイラ依存（GCC/Clang/MSVC全て対応）。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="intel-tbbthread-building-blocks"&gt;Intel TBB（Thread Building Blocks）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;動的なタスクスケジューリング・負荷分散・ワークスティーリングが可能。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;不規則な処理を高速並列化したいときに強力。&lt;/p&gt;</description></item><item><title>💡 C++ の emplace_back は push_back を完全に置き換えられるか？</title><link>https://wuyukwi.duckdns.org/posts/zenn-c73aa0d12885ff/</link><pubDate>Tue, 24 Jun 2025 12:40:20 +0900</pubDate><guid>https://wuyukwi.duckdns.org/posts/zenn-c73aa0d12885ff/</guid><description>&lt;blockquote&gt;
&lt;p&gt;📌 &lt;em&gt;この記事は &lt;a href="https://zenn.dev/wuyukwi/articles/c73aa0d12885ff"&gt;Zenn&lt;/a&gt; に投稿した内容のアーカイブです。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="はじめに"&gt;はじめに&lt;/h2&gt;
&lt;p&gt;C++11 から導入された &lt;code&gt;emplace_back&lt;/code&gt; は、コンテナに要素をその場で構築できる便利なメソッドです。&lt;br&gt;
一方、それ以前から使われてきた &lt;code&gt;push_back&lt;/code&gt; とはどんな違いがあり、&lt;code&gt;emplace_back&lt;/code&gt; は完全に &lt;code&gt;push_back&lt;/code&gt; を代替できるのでしょうか？&lt;/p&gt;
&lt;p&gt;本記事では、&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;emplace_back&lt;/code&gt; が本当に &lt;code&gt;push_back&lt;/code&gt; より常に速いのか&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;両者がどんなケースに使えるのか&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;実装の違いと注意点&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;について、自分なりに整理した考えをまとめます。&lt;/p&gt;
&lt;h2 id="push_back-と-emplace_back-の違い"&gt;&lt;code&gt;push_back&lt;/code&gt; と &lt;code&gt;emplace_back&lt;/code&gt; の違い&lt;/h2&gt;
&lt;h3 id="push_back"&gt;&lt;code&gt;push_back&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// C++20以降
constexpr void push_back(const T&amp;amp; value); // コピー
constexpr void push_back(T&amp;amp;&amp;amp; value); // ムーブ
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;push_back&lt;/code&gt; は、すでに生成されたオブジェクトをコピーまたはムーブして格納する関数です。&lt;/p&gt;
&lt;h3 id="emplace_back"&gt;&lt;code&gt;emplace_back&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// C++20以降
template&amp;lt;class... Args&amp;gt;
constexpr reference emplace_back(Args&amp;amp;&amp;amp;... args); // その場で構築
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;emplace_back&lt;/code&gt; は、コンストラクタに渡す引数をそのまま受け取り、コンテナ内部で直接オブジェクトを構築します。そのため、&lt;strong&gt;中間オブジェクトなし&lt;/strong&gt; に要素を追加できる点が特徴です。&lt;/p&gt;
&lt;p&gt;これらの大きな違いは、&lt;code&gt;push_back&lt;/code&gt;が&lt;strong&gt;既に生成されたオブジェクト（T型）&lt;strong&gt;をコピーまたはムーブする一方で、&lt;code&gt;emplace_back&lt;/code&gt;は&lt;/strong&gt;任意のコンストラクタ引数からその場でT型のインスタンスを構築できる&lt;/strong&gt;点です。&lt;/p&gt;
&lt;h2 id="llvmにおける内部実装"&gt;LLVMにおける内部実装&lt;/h2&gt;
&lt;p&gt;以下はLLVM(libc++)における&lt;code&gt;push_back&lt;/code&gt;内部の実装です。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (this-&amp;gt;__end_ != this-&amp;gt;__end_cap()) {
__construct_one_at_end(__x); // 残り容量がある場合、__xをコピーまたはムーブ構築
} else {
__push_back_slow_path(__x); // 足りない場合、新規領域を確保して再配置
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;__construct_one_at_end&lt;/code&gt; の中では、allocatorが提供する &lt;code&gt;construct()&lt;/code&gt; を使って、指定されたメモリにオブジェクトを構築しています。&lt;br&gt;
また、allocatorが &lt;code&gt;construct()&lt;/code&gt; を持たない場合には単純に in-place new を呼び出す仕組みです。&lt;/p&gt;</description></item><item><title>🎉 std::function がヒープメモリを確保する条件とは？— コピーキャプチャされた変数の型がカギ</title><link>https://wuyukwi.duckdns.org/posts/zenn-fdcfc181acf35d/</link><pubDate>Thu, 05 Jun 2025 13:03:20 +0900</pubDate><guid>https://wuyukwi.duckdns.org/posts/zenn-fdcfc181acf35d/</guid><description>&lt;blockquote&gt;
&lt;p&gt;📌 &lt;em&gt;この記事は &lt;a href="https://zenn.dev/wuyukwi/articles/fdcfc181acf35d"&gt;Zenn&lt;/a&gt; に投稿した内容のアーカイブです。&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="はじめに"&gt;はじめに&lt;/h2&gt;
&lt;p&gt;C++ の &lt;code&gt;std::function&lt;/code&gt; は「小さいオブジェクトならヒープ確保しない（Small Object Optimization, SOO）」という知識は一般的です。しかし、以下のようなコードでは意外な挙動が観察されます：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct A {
int value;
A(int v) : value(v) {}
A(const A &amp;amp;other) : value(other.value) {}
};
struct B {
int value;
B(int v) : value(v) {}
};
A a{0};
B b{0};
std::function&amp;lt;void()&amp;gt; funcA = [a] {}; // ヒープ確保あり
std::function&amp;lt;void()&amp;gt; funcB = [b] {}; // ヒープ確保なし
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;このように、&lt;strong&gt;コピーコンストラクタの有無だけ&lt;/strong&gt; で挙動が変わるのはなぜでしょうか？&lt;/p&gt;
&lt;h2 id="stdfunction-がローカル格納を行う条件gcclibstdc-編"&gt;&lt;code&gt;std::function&lt;/code&gt; がローカル格納を行う条件（GCC/libstdc++ 編）&lt;/h2&gt;
&lt;p&gt;libstdc++ の実装（GCC 標準ライブラリ）では、以下の条件を満たすとオブジェクトはヒープではなくローカルに保存されます：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;static const bool __stored_locally =
(__is_location_invariant&amp;lt;_Functor&amp;gt;::value &amp;amp;&amp;amp;
sizeof(_Functor) &amp;lt;= _M_max_size &amp;amp;&amp;amp;
__alignof__(_Functor) &amp;lt;= _M_max_align &amp;amp;&amp;amp;
(_M_max_align % __alignof__(_Functor) == 0));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;この中で特に重要なのが：&lt;/p&gt;</description></item></channel></rss>