Bir kitaba başvurmadan, lütfen CRTP
bir kod örneği ile iyi bir açıklama yapabilir misiniz?
Bir kitaba başvurmadan, lütfen CRTP
bir kod örneği ile iyi bir açıklama yapabilir misiniz?
Yanıtlar:
Kısacası, CRTP, bir sınıfın A
, sınıfın A
kendisi için bir şablon uzmanlığı olan temel bir sınıfı olduğu zamandır . Örneğin
template <class T>
class X{...};
class A : public X<A> {...};
O edilir merakla, değil mi yinelenen? :)
Şimdi, bu size ne veriyor? Bu aslında X
şablona uzmanlıkları için bir temel sınıf olma yeteneği verir .
Örneğin, böyle bir genel singleton sınıfı (basitleştirilmiş sürüm) yapabilirsiniz
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;
Şimdi, keyfi bir sınıfı A
tekil yapmak için bunu yapmalısınız
class A: public Singleton<A>
{
//Rest of functionality for class A
};
Gördün mü? Singleton şablonu, herhangi bir tür için uzmanlığının X
miras singleton<X>
alınacağını ve böylece de dahil olmak üzere tüm (genel, korunan) üyelerin erişilebilir olmasını varsayar GetInstance
. CRTP'nin başka yararlı kullanımları da vardır. Örneğin, şu anda sınıfınız için var olan tüm örnekleri saymak istiyorsanız, ancak bu mantığı ayrı bir şablonda kapsüllemek istiyorsanız (somut bir sınıf fikri oldukça basittir - statik bir değişkene sahip olmak, aktörlerde artış, dtors'ta azalma ). Bir egzersiz olarak yapmaya çalışın!
Boost için yine bir başka yararlı örnek (nasıl uyguladıklarından emin değilim, ancak CRTP de yapacak). <
Sınıflarınız için yalnızca operatör sağlamak ==
, onlar için de otomatik olarak operatör sağlamak istediğinizi düşünün !
şöyle yapabilirsin:
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 <
}
Şimdi bu şekilde kullanabilirsiniz
struct Apple:public Equality<Apple>
{
int size;
};
bool operator < (Apple const & a1, Apple const& a2)
{
return a1.size < a2.size;
}
Şimdi, açıkça operatörü sunmadınız ==
için Apple
? Ama sende var! Yazabilirsin
int main()
{
Apple a1;
Apple a2;
a1.size = 10;
a2.size = 10;
if(a1 == a2) //the compiler won't complain!
{
}
}
Bu size sadece operatörü yazdıysam az yazacağını görünebilir ==
için Apple
, ama bu hayal Equality
şablon sadece sağlayacak ==
ama >
, >=
, <=
vs Ve bu tanımları kullanabilirsiniz çoklu kod yeniden, sınıflar!
CRTP harika bir şey :) HTH
Burada harika bir örnek görebilirsiniz. Sanal yöntem kullanırsanız, program çalışma zamanında neyin yürütüldüğünü bilecektir. Derleyici CRTP uygulamak derleme zamanında karar hangi !!! Bu harika bir performans!
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);
}
};
virtual void write(const char* str) const = 0;
mısın? Adil olmakla birlikte, bu teknik write
başka işler yaparken süper yararlı görünüyor .
CRTP, derleme zamanı polimorfizmini uygulamak için bir tekniktir. İşte çok basit bir örnek. Aşağıdaki örnekte, sınıf arabirimi ProcessFoo()
ile çalışıyor Base
ve sanal yöntemlerle yapmayı amaçladığınız Base::Foo
türetilmiş nesnenin foo()
yöntemini çağırıyor .
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;
}
Çıktı:
derived foo
AnotherDerived foo
foo()
türetilmiş sınıf tarafından uygulanan zorunluluğudur .
ProcessFoo()
işlevle neden yararlı olduğunu da gösteriyor .
void ProcessFoo(T* b)
ve olmadan Derived ve AnotherDerived aslında türetilmiş hala işe yarayacaktı. IMHO, ProcessFoo'nun bir şekilde şablonları kullanmaması daha ilginç olurdu.
ProcessFoo()
arabirimi uygulayan herhangi bir tür ile çalışacaktır, yani bu durumda T giriş türü denilen bir yönteme sahip olmalıdır foo()
. İkincisi, ProcessFoo
birden fazla tiple çalışmak için temassız hale gelmek için , muhtemelen kaçınmak istediğimiz RTTI'yi kullanacaksınız. Ayrıca, templatized sürümü arayüz derleme süresi kontrolü sağlar.
Bu doğrudan bir cevap değil, CRTP'nin nasıl faydalı olabileceğinin bir örneğidir .
İyi bir somut örneği cRTP olan std::enable_shared_from_this
C ++ 11:
Bir sınıf
T
, aşağıdakilerienable_shared_from_this<T>
alanshared_from_this
üye işlevlerini devralmak içinshared_ptr
için örnek işaret*this
.
Yani, miras std::enable_shared_from_this
örneğinize erişmeden paylaşılan (veya zayıf) bir işaretçi almayı mümkün kılar (örneğin, yalnızca bildiğiniz bir üye işlevinden)*this
).
Bir vermeniz gerektiğinde yararlıdır, std::shared_ptr
ancak yalnızca*this
:
struct Node;
void process_node(const std::shared_ptr<Node> &);
struct Node : std::enable_shared_from_this<Node> // CRTP
{
std::weak_ptr<Node> parent;
std::vector<std::shared_ptr<Node>> children;
void add_child(std::shared_ptr<Node> child)
{
process_node(shared_from_this()); // Shouldn't pass `this` directly.
child->parent = weak_from_this(); // Ditto.
children.push_back(std::move(child));
}
};
Bunun this
yerine doğrudan geçememenizin nedeni shared_from_this()
, sahiplik mekanizmasını bozmasıdır:
struct S
{
std::shared_ptr<S> get_shared() const { return std::shared_ptr<S>(this); }
};
// Both shared_ptr think they're the only owner of S.
// This invokes UB (double-free).
std::shared_ptr<S> s1 = std::make_shared<S>();
std::shared_ptr<S> s2 = s1->get_shared();
assert(s2.use_count() == 1);
Not:
CRTP, statik polimorfizmi uygulamak için kullanılabilir (ki bu dinamik polimorfizmi sever ancak sanal fonksiyon işaret tablosu olmadan).
#pragma once
#include <iostream>
template <typename T>
class Base
{
public:
void method() {
static_cast<T*>(this)->method();
}
};
class Derived1 : public Base<Derived1>
{
public:
void method() {
std::cout << "Derived1 method" << std::endl;
}
};
class Derived2 : public Base<Derived2>
{
public:
void method() {
std::cout << "Derived2 method" << std::endl;
}
};
#include "crtp.h"
int main()
{
Derived1 d1;
Derived2 d2;
d1.method();
d2.method();
return 0;
}
Çıktı:
Derived1 method
Derived2 method
vtable
CRTP kullanılmadan yapılabilir . Ne vtable
gerçekten sağlayan s türetilmiş yöntemleri çağırmak temel sınıf (işaretçi ya da referansları) kullanıyor. CRTP ile nasıl yapıldığını burada göstermelisiniz.
Base<>::method ()
, hiç çağrılmıyor, hatta hiçbir yerde polimorfizm kullanmıyorsunuz.
methodImpl
içinde method
bir Base
ve türetilmiş sınıfları isim methodImpl
yerinemethod