<질문>
책을 참조하지 않고 코드 예제와 함께 CRTP에 대한 좋은 설명을 제공 할 수 있습니까?
<답변1>
요컨대, CRTP는 클래스 A가 클래스 A 자체에 대한 템플릿 전문화 인 기본 클래스를 갖는 경우입니다. 예
template
class X{...};
class A : public X {...};
이상하게 되풀이되지 않나요? :)
자, 이것은 당신에게 무엇을 제공합니까? 이것은 실제로 X 템플릿에 해당 전문화에 대한 기본 클래스가 될 수있는 기능을 제공합니다.
예를 들어 다음과 같은 일반 싱글 톤 클래스 (단순 버전)를 만들 수 있습니다.
template
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
T* Singleton::p = nullptr;
이제 임의의 클래스 A를 싱글 톤으로 만들려면 다음을 수행해야합니다.
class A: public Singleton
{
//Rest of functionality for class A
};
그래서 알 겠어요? 싱글 톤 템플릿은 모든 유형 X에 대한 전문화가 싱글 톤에서 상속된다고 가정합니다.
Boost의 또 다른 유용한 예입니다 (어떻게 구현했는지 잘 모르겠지만 CRTP도 그렇게 할 것입니다). 클래스에는 <연산자 만 제공하고 클래스에는 자동으로 == 연산자를 제공하고 싶다고 상상해보십시오!
다음과 같이 할 수 있습니다.
template
class Equality
{
};
template
bool operator == (Equality const& op1, Equality const & op2)
{
Derived const& d1 = static_cast(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(op2);
return !(d1 < d2) && !(d2 < d1);//assuming derived has operator <
}
이제 이렇게 사용할 수 있습니다
struct Apple:public Equality
{
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에 대해 operator ==를 작성하면 덜 작성하는 것처럼 보일 수 있지만 Equality 템플릿이 ==뿐만 아니라>,> =, <= 등을 제공한다고 상상해보십시오. 여러 클래스에 대해 이러한 정의를 사용할 수 있습니다. 코드 재사용!
CRTP는 멋진 것입니다 :) HTH
<답변2>
여기에서 훌륭한 예를 볼 수 있습니다. 가상 방법을 사용하는 경우 프로그램은 런타임에서 실행되는 항목을 인식합니다. CRTP를 구현하면 컴파일러가 컴파일 시간을 결정합니다 !!! 이것은 대단한 성능입니다!
template
class Writer
{
public:
Writer() { }
~Writer() { }
void write(const char* str) const
{
static_cast(this)->writeImpl(str); //here the magic is!!!
}
};
class FileWriter : public Writer
{
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
{
public:
ConsoleWriter() { }
~ConsoleWriter() { }
void writeImpl(const char* str) const
{
printf("%s\n", str);
}
};
<답변3>
CRTP는 컴파일 타임 다형성을 구현하는 기술입니다. 여기 아주 간단한 예가 있습니다. 아래 예제에서 ProcessFoo ()는 Base 클래스 인터페이스와 함께 작동하고 Base :: Foo는 가상 메서드로 수행하려는 작업 인 파생 객체의 foo () 메서드를 호출합니다.
http://coliru.stacked-crooked.com/a/2d27f1e09d567d0e
template
struct Base {
void foo() {
(static_cast(this))->foo();
}
};
struct Derived : public Base {
void foo() {
cout << "derived foo" << endl;
}
};
struct AnotherDerived : public Base {
void foo() {
cout << "AnotherDerived foo" << endl;
}
};
template
void ProcessFoo(Base* b) {
b->foo();
}
int main()
{
Derived d1;
AnotherDerived d2;
ProcessFoo(&d1);
ProcessFoo(&d2);
return 0;
}
산출:
derived foo
AnotherDerived foo
<답변4>
이것은 직접적인 대답이 아니라 CRTP가 어떻게 유용 할 수 있는지에 대한 예입니다.
CRTP의 좋은 구체적인 예는 C ++ 11의 std :: enable_shared_from_this입니다.
[util.smartptr.enab] / 1 클래스 T는 enable_shared_from_this에서 상속 할 수 있습니다.
* this를 가리키는 shared_ptr 인스턴스를 얻는 shared_from_this 멤버 함수를 상속합니다.
즉, std :: enable_shared_from_this에서 상속하면 인스턴스에 대한 액세스없이 공유 (또는 약한) 포인터를 얻을 수 있습니다 (예 : * this에 대해서만 알고있는 멤버 함수에서).
std :: shared_ptr을 제공해야 할 때 유용하지만 * this에만 액세스 할 수 있습니다.
struct Node;
void process_node(const std::shared_ptr &);
struct Node : std::enable_shared_from_this // CRTP
{
std::weak_ptr parent;
std::vector> children;
void add_child(std::shared_ptr child)
{
process_node(shared_from_this()); // Shouldn't pass `this` directly.
child->parent = weak_from_this(); // Ditto.
children.push_back(std::move(child));
}
};
shared_from_this () 대신 이것을 직접 전달할 수없는 이유는 소유권 메커니즘을 깨기 때문입니다.
struct S
{
std::shared_ptr get_shared() const { return std::shared_ptr(this); }
};
// Both shared_ptr think they're the only owner of S.
// This invokes UB (double-free).
std::shared_ptr s1 = std::make_shared();
std::shared_ptr s2 = s1->get_shared();
assert(s2.use_count() == 1);
<답변5>
참고 사항 :
CRTP는 정적 다형성을 구현하는 데 사용할 수 있습니다 (동적 다형성과 비슷하지만 가상 함수 포인터 테이블이 없음).
#pragma once
#include
template
class Base
{
public:
void method() {
static_cast(this)->method();
}
};
class Derived1 : public Base
{
public:
void method() {
std::cout << "Derived1 method" << std::endl;
}
};
class Derived2 : public Base
{
public:
void method() {
std::cout << "Derived2 method" << std::endl;
}
};
#include "crtp.h"
int main()
{
Derived1 d1;
Derived2 d2;
d1.method();
d2.method();
return 0;
}
출력은 다음과 같습니다.
Derived1 method
Derived2 method
'개발 > C++' 카테고리의 다른 글
if… else if 문을 확률로 정렬하면 어떤 효과가 있습니까? (0) | 2020.09.30 |
---|---|
보호 또는 개인 생성자 만있는 클래스에서 :: std :: make_shared를 어떻게 호출합니까? (0) | 2020.09.30 |
함수 템플릿의 기본 템플릿 인수 (0) | 2020.09.30 |
4x4 행렬 반전 (0) | 2020.09.26 |