Pynote

Python、機械学習、画像処理について

C++ - The Curiously Recurring Template Pattern (CRTP)

概要

The Curiously Recurring Template Pattern (CRTP) とはテンプレートを利用したテクニックで日本語では、奇妙に再帰したテンプレートパターンと訳される。
内容としては次のものである。

  • テンプレートクラスを継承する。
  • 継承した子クラスを基底クラスのテンプレート引数に指定する。
template <typename T>
class Base
{
    ...
};
 
class Derived : public Base<Derived>
{
    ...
};

こうすることで基底クラス内で派生クラスを利用することができる。
*this を T& にダウンキャストするにあたって、dynamic_cast でなく、static_cast でよい。
なぜなら、Base クラスのテンプレート引数に派生クラス Derived を与えるように設計したからである。

template <typename T>
class Base
{
public:
    void doSomething()
    {
        T& derived = static_cast<T&>(*this);
        // 派生クラスを利用するコード
    }
};

利用用途

静的なポリモーフィズム
#include <iostream>

template <class T>
struct Base
{
    void interface()
    {
        static_cast<T *>(this)->implementation();
    }
};

struct Derived1 : Base<Derived1>
{
    void implementation()
    {
        std::cout << "Derived1" << std::endl;
    }
};

struct Derived2 : Base<Derived2>
{
    void implementation()
    {
        std::cout << "Derived2" << std::endl;
    }
};

int main(int argc, char *argv[])
{
    Derived1().interface();
    Derived2().interface();
}

出力結果

Derived1
Derived2
仮想関数を使用する例

同じことは仮想関数を使ってもできる。
両者の違いは、CRTP を使用した場合はコンパイル時に行われるのに対し、仮想関数の場合は実行時に行われる。

#include <iostream>

struct Base
{
    void interface()
    {
        implementation();
    }

    virtual void implementation() = 0;
};

struct Derived1 : Base
{
    void implementation()
    {
        std::cout << "Derived1" << std::endl;
    }
};

struct Derived2 : Base
{
    void implementation()
    {
        std::cout << "Derived2" << std::endl;
    }
};

int main(int argc, char *argv[])
{
    Derived1().interface();
    Derived2().interface();
}