💡 C++ の「奇妙な再帰テンプレートパターン(CRTP)」を理解する

📌 この記事は Zenn に投稿した内容のアーカイブです。 はじめに C++ のテンプレートには数多くの応用テクニックがありますが、その中でも「CRTP(Curiously Recurring Template Pattern:奇妙な再帰テンプレートパターン) 」は特にユニークで強力な手法です。 Boost をはじめとする有名ライブラリでも多用されており、C++ の表現力を大きく広げてくれます。 この記事では、CRTP の基本から実用例、さらには応用パターンまでを整理して解説します。 CRTP とは? CRTP の基本形は以下のようになります。 template <typename T> class Base { public: T& getDerived() { return static_cast<T&>(*this); } }; class Derived : public Base<Derived> { // Derived 自身を Base のテンプレート引数として渡す }; 一見すると「派生クラス Derived が、まだ完成していない自分自身を基底クラス Base に渡している」ように見えます。この“自己参照”こそが CRTP の特徴です。 コンパイラはこれを問題なく処理でき、基底クラスはコンパイル時に派生クラスの型情報を知ることができます。 実用例:比較演算子の自動生成 C++ では == や < などの比較演算子を定義するのはよくある作業ですが、すべてを個別に書くと冗長になりがちです。 CRTP を用いると、基底クラスに「共通の比較演算子」をまとめ、派生クラスは最小限の実装だけで済ませられます。 基底クラス template <typename Derived> class Comparable { public: bool operator==(const Derived& other) const { return static_cast<const Derived*>(this)->equalsImpl(other); } bool operator!=(const Derived& other) const { return !(*this == other); } bool operator<(const Derived& other) const { return static_cast<const Derived*>(this)->lessImpl(other); } bool operator>(const Derived& other) const { return other < *this; } bool operator<=(const Derived& other) const { return !(*this > other); } bool operator>=(const Derived& other) const { return !(*this < other); } }; 派生クラス例 class Point : public Comparable<Point> { public: Point(int x_val, int y_val) : x(x_val), y(y_val) {} bool equalsImpl(const Point& other) const { return x == other.x && y == other.y; } bool lessImpl(const Point& other) const { return (x != other.x) ? (x < other.x) : (y < other.y); } private: int x, y; }; これで ==, !=, <, >, <=, >= がすべて利用可能になります。 ...

2025年9月30日 · 2 分 · 235 文字 · Wuyukwi

🔥 ゲームプログラマーとして成長するために大切な3つの力

📌 この記事は Zenn に投稿した内容のアーカイブです。 はじめに 「どうすればプログラミングが上手くなるのか?」 これは、ゲーム業界を目指すエンジニアにとって、常につきまとうテーマです。 Unity や Unreal Engine などの便利なエンジンやライブラリが整っている時代でも、「本当にゲームを作れる」プログラマーになるためには、言語やフレームワークの知識以上に重要な根本的スキル があります。 本記事では、ゲームプログラマーとして実務を経験する中で特に大事だと感じる3つの力 を紹介します。 ① 抽象化の力 —— 巨大なコードを「分けて」考える ゲーム開発は、数千〜数百万行にもなるコードの集合です。 その全てを頭に入れることは不可能なので、必要な部分だけをブラックボックスとして扱う 考え方が必須になります。 例えば、「プレイヤーが持つ全ての武器を取得する」処理を作る場合を考えます。 // Repositoryパターンによる例 class WeaponRepository { public List<Weapon> GetAllWeapons(PlayerId playerId); } この WeaponRepository クラスを作るときは、 DBやセーブデータへのアクセス ログやエラーハンドリング フォーマット変換 など、たくさんの下位レイヤのライブラリを呼び出しますが、上のレイヤからは「GetAllWeapons」だけ見えればよい のです。 これが抽象化であり、モジュールを積み重ねて大規模ゲームを成立させる基本 です。 実戦ポイント オブジェクト指向(OOP)、関数型(FP)、手続き型といった 複数のパラダイムを状況に応じて使い分ける トップダウン(設計から実装)とボトムアップ(共通処理の抽出)を行き来しながら開発する 抽象化しすぎると開発が重くなり、抽象化しなさすぎるとコピペ地獄になるため、バランスを取る ② デバッグの力 —— ブラックボックスを「開ける」技術 抽象化したコードは便利ですが、バグが起きたときには中を開ける必要 があります。 たとえば「インベントリ画面に武器一覧が表示されない」と報告されたとき、どこから原因を追うべきでしょうか? F12(DevTools)や curl でクライアント→サーバ間通信を確認 サーバで WeaponRepository.GetAllWeapons() の戻り値をログ出力 DBにクエリを直打ちして期待通りの結果か確認 こうして層を一つずつ下って調査 するのがデバッグの基本です。 このスキルはバグ修正だけでなく、新しいプロジェクトやチームに参加したときにコードを理解する力 にもなります。 実戦ポイント ログやアサートを常に入れておく デバッガでステップ実行できるように設計する バグ報告を「再現可能な条件」に落とし込む ...

2025年9月18日 · 1 分 · 102 文字 · Wuyukwi