C++ の「奇妙な再帰テンプレートパターン(CRTP)」を理解する
はじめに
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;
};
これで ==
, !=
, <
, >
, <=
, >=
がすべて利用可能になります。
CRTP の利点
-
静的ポリモーフィズム
仮想関数を使わずに「派生クラスが特定のメソッドを持つ」ことをコンパイル時に保証できる。 -
ゼロコスト抽象化
実行時の仮想関数呼び出し(vtable 参照)を避け、インライン展開が可能。 -
コード再利用
共通処理を基底クラスに集約し、派生クラスは本質的な処理のみを書く。
応用パターン
CRTP は比較演算子以外にもさまざまな用途があります。
-
Mixin の実装
-
ロギング機能を自動付与
-
インスタンス数カウンタ
-
シングルトン制約
-
-
ポリシーベースデザイン
-
メモリ管理戦略
-
スレッドセーフ戦略
-
-
インターフェース強制
- 派生クラスに特定のメソッド実装をコンパイル時に強制
-
イテレータ実装支援
- Boost の
iterator_facade
が代表例
- Boost の
まとめ
CRTP は「基底クラスが派生クラスを知っている」という一見逆説的な構造を活かし、静的ポリモーフィズムやコード再利用を実現する強力なテクニックです。
C++ を本格的に使いこなしたい人にとって必須の知識であり、Boost をはじめとする実用ライブラリでも広く応用されています。
「奇妙」な見た目に反して、その応用範囲と恩恵は非常に大きい。これを理解し使いこなせば、あなたの C++ コードはさらに強力で表現力豊かになるでしょう。
Discussion