CRTP パターンは通常、静的ポリモーフィズムとmixin (パラメーター化された) 動作を有効にするために使用されます。2 つの選択肢を説明するには、最初に一般的なテンプレートを定義すると便利です
template
<
typename Derived
>
class enable_down_cast
{
private:
// typedefs
typedef enable_down_cast Base;
public:
Derived const* self() const
{
// casting "down" the inheritance hierarchy
return static_cast<Derived const*>(this);
}
// write the non-const version in terms of the const version
// Effective C++ 3rd ed., Item 3 (p. 24-25)
Derived* self()
{
return const_cast<Derived*>(static_cast<Base const*>(this)->self());
}
protected:
// disable deletion of Derived* through Base*
// enable deletion of Base* through Derived*
~enable_down_cast() = default; // C++11 only, use ~enable_down_cast() {} in C++98
};
次に、必要な動作のタイプのインターフェイス クラス テンプレートを定義します。
template<typename FX>
class FooInterface
:
// enable static polymorphism
public enable_down_cast< FX >
{
private:
// dependent name now in scope
using enable_down_cast< FX >::self;
public:
// interface
void foo() { self()->do_foo(); }
protected:
// disable deletion of Derived* through Base*
// enable deletion of Base* through Derived*
~IFooInterface() = default; // C++11 only, use ~IFooInterface() {} in C++98/03
};
このインターフェースのさまざまな実装を取得するには、単純に、それぞれが不思議なことに繰り返されるテンプレート パラメーターFooInterface
として派生するさまざまなクラスを定義します。
class FooImpl
:
public FooInterface< FooImpl >
{
private:
// implementation
friend class FooInterface< FooImpl > ;
void do_foo() { std::cout << "Foo\n"; }
};
class AnotherFooImpl
:
public FooInterface< AnotherFooImpl >
{
private:
// implementation
friend class FooInterface< AnotherFooImpl >;
void do_foo() { std::cout << "AnotherFoo\n"; }
};
別の方法は、インターフェイスのさまざまな実装をパラメーター化することです。今回は、クラス テンプレートはtemplate-template パラメーターと非型パラメーターの両方に依存します。
template<template<int> class F, int X>
class BarInterface
:
public enable_down_cast< F<X> >
{
private:
// dependent name now in scope
using enable_down_cast< F<X> >::self;
public:
// interface
void bar() { self()->do_bar(); }
protected:
// disable deletion of Derived* through Base*
// enable deletion of Base* through Derived*
~BarInterface() = default; // C++11 only, use ~BarInterface() {} in C++98/03
};
実装は別のクラス テンプレートであり、それ自体と非型パラメーターの両方を引数として使用してインターフェイスから派生します。
template< int X >
class BarImpl
:
public BarInterface< BarImpl, X >
{
private:
// implementation
friend class BarInterface< ::BarImpl, X >;
void do_bar() { std::cout << X << "\n"; }
};
これはあなたがそれらを呼び出す方法です:
int main()
{
FooImpl f1;
AnotherFooImpl f2;
BarImpl< 1 > b1;
BarImpl< 2 > b2;
f1.foo();
f2.foo();
b1.bar();
b2.bar();
return 0;
}
あなたの質問のクラスは、この一般的なパターンに完全には適合しません。Derived
CRTP のような動作をさせたい場合は、次のいずれかを実行できます。
class Derived1
:
public CRTP< Derived1 >
{
};
template<int I>
class Derived2
:
public CRTPInt< Derived2, I >
{
};
更新: https://stackoverflow.com/a/11571808/819272からの議論に基づいて、元の回答は Visual Studio 2010 でのみコンパイルされ、Microsoft 固有の移植性のない機能のために gcc ではコンパイルされないことがわかりました。たとえば、self()
関数 fromはその派生クラスの (テンプレート) 依存名であるため、明示的なディレクティブenable_down_cast
なしでは表示されません。using
さらに、適切なレベルの保護を備えたデフォルトのデストラクタを追加しました。最後に、元のクラスの名前を に変更enable_crtp
しましenable_down_cast
た。これは、コンパイラが動的ポリモーフィズムに対して自動的に行うことを、静的ポリモーフィズムに対して手動で有効にするためです。