本を参照せずに、誰かCRTP
がコード例で良い説明を提供できますか?
6 に答える
つまり、CRTPは、クラス自体A
のテンプレートに特化した基本クラスがクラスにある場合です。A
例えば
template <class T>
class X{...};
class A : public X<A> {...};
不思議なことに繰り返されていますね。:)
さて、これはあなたに何を与えますか?これにより、実際には、X
テンプレートがその特殊化の基本クラスになることができます。
たとえば、次のような汎用シングルトンクラス(簡略化バージョン)を作成できます。
template <class ActualClass>
class Singleton
{
public:
static ActualClass& GetInstance()
{
if(p == nullptr)
p = new ActualClass;
return *p;
}
protected:
static ActualClass* p;
private:
Singleton(){}
Singleton(Singleton const &);
Singleton& operator = (Singleton const &);
};
template <class T>
T* Singleton<T>::p = nullptr;
さて、任意のクラスA
をシングルトンにするためには、これを行う必要があります
class A: public Singleton<A>
{
//Rest of functionality for class A
};
ご覧のように?X
シングルトンテンプレートは、任意のタイプの特殊化が継承されることを前提としているため、 !singleton<X>
を含むすべての(パブリック、保護された)メンバーにアクセスできます。GetInstance
CRTPの他の便利な使用法があります。たとえば、クラスに現在存在するすべてのインスタンスをカウントしたいが、このロジックを別のテンプレートにカプセル化したい場合(具体的なクラスのアイデアは非常に単純です-静的変数、ctorsのインクリメント、dtorsのデクリメントがあります)。練習としてやってみてください!
Boostのさらに別の便利な例(彼らがそれをどのように実装したかはわかりませんが、CRTPもそうします)。<
クラスには演算子のみを提供し、クラスには自動的に演算子を提供したいとし==
ます。
あなたはこのようにそれを行うことができます:
template<class Derived>
class Equality
{
};
template <class Derived>
bool operator == (Equality<Derived> const& op1, Equality<Derived> const & op2)
{
Derived const& d1 = static_cast<Derived const&>(op1);//you assume this works
//because you know that the dynamic type will actually be your template parameter.
//wonderful, isn't it?
Derived const& d2 = static_cast<Derived const&>(op2);
return !(d1 < d2) && !(d2 < d1);//assuming derived has operator <
}
これで、このように使用できます
struct Apple:public Equality<Apple>
{
int size;
};
bool operator < (Apple const & a1, Apple const& a2)
{
return a1.size < a2.size;
}
さて、あなたは?の演算子==
を明示的に提供していません。Apple
しかし、あなたはそれを持っています!あなたは書ける
int main()
{
Apple a1;
Apple a2;
a1.size = 10;
a2.size = 10;
if(a1 == a2) //the compiler won't complain!
{
}
}
==
これは、の演算子を記述しただけでは記述が少なくなるように見えるかもしれませんが、テンプレートが、、などを提供するだけでなく、コードを再利用して、これらの定義を複数のApple
クラスに使用できると想像してください。Equality
==
>
>=
<=
CRTPは素晴らしいものです:)HTH
ここで素晴らしい例を見ることができます。仮想メソッドを使用すると、プログラムは実行時に何が実行されるかを認識します。コンパイラがコンパイル時に決定するCRTPの実装!!! これは素晴らしいパフォーマンスです!
template <class T>
class Writer
{
public:
Writer() { }
~Writer() { }
void write(const char* str) const
{
static_cast<const T*>(this)->writeImpl(str); //here the magic is!!!
}
};
class FileWriter : public Writer<FileWriter>
{
public:
FileWriter(FILE* aFile) { mFile = aFile; }
~FileWriter() { fclose(mFile); }
//here comes the implementation of the write method on the subclass
void writeImpl(const char* str) const
{
fprintf(mFile, "%s\n", str);
}
private:
FILE* mFile;
};
class ConsoleWriter : public Writer<ConsoleWriter>
{
public:
ConsoleWriter() { }
~ConsoleWriter() { }
void writeImpl(const char* str) const
{
printf("%s\n", str);
}
};
CRTP は、コンパイル時のポリモーフィズムを実装する手法です。これは非常に簡単な例です。以下の例でProcessFoo()
は、Base
クラス インターフェイスを操作しBase::Foo
、派生オブジェクトのfoo()
メソッドを呼び出します。これは、仮想メソッドで行うことを目的としています。
http://coliru.stacked-crooked.com/a/2d27f1e09d567d0e
template <typename T>
struct Base {
void foo() {
(static_cast<T*>(this))->foo();
}
};
struct Derived : public Base<Derived> {
void foo() {
cout << "derived foo" << endl;
}
};
struct AnotherDerived : public Base<AnotherDerived> {
void foo() {
cout << "AnotherDerived foo" << endl;
}
};
template<typename T>
void ProcessFoo(Base<T>* b) {
b->foo();
}
int main()
{
Derived d1;
AnotherDerived d2;
ProcessFoo(&d1);
ProcessFoo(&d2);
return 0;
}
出力:
derived foo
AnotherDerived foo