흥미로운 반복 템플릿 패턴 (CRTP)은 무엇입니까? CRTP코드 예제 를 통해

책을 언급하지 않고 누구나 CRTP코드 예제 를 통해 좋은 설명을 제공 할 수 있습니까?



답변

간단히 말해서, 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의 다른 유용한 용도가 있습니다. 예를 들어, 클래스에 현재 존재하는 모든 인스턴스를 세고 싶지만이 로직을 별도의 템플릿으로 캡슐화하려는 경우 (구체적인 클래스에 대한 아이디어는 매우 간단합니다. 정적 변수, ctor 단위로 증가, 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()로 작업 하고 파생 객체의 메소드를 호출합니다 . 이는 가상 메소드로 수행하려는 것입니다.BaseBase::Foofoo()

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


답변

이것은 직접적인 대답이 아니라 CRTP 가 유용한 방법의 예입니다 .


의 좋은 콘크리트 예를 CRTP는 이다 std::enable_shared_from_thisC ++ 11 :

[util.smartptr.enab] / 1

클래스 T는를 가리키는 인스턴스 를 얻는 멤버 함수 enable_­shared_­from_­this<T>를 상속하기 위해 상속 할 수 있습니다 .shared_­from_­thisshared_­ptr*this

즉,에서 상속 std::enable_shared_from_this하면 인스턴스에 액세스하지 않고 인스턴스에 대한 공유 (또는 약한) 포인터를 얻을 수 있습니다 (예 : 만 알고있는 멤버 함수에서 *this).

당신이 줄 필요가 std::shared_ptr있지만 다음에 액세스 할 수있을 때 유용 합니다 *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));
    }
};

this대신 직접 전달할 수없는 이유 shared_from_this()는 소유권 메커니즘을 위반하기 때문입니다.

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);


답변

참고로

CRTP는 정적 다형성 (동적 다형성과 유사하지만 가상 함수 포인터 테이블이 없음)을 구현하는 데 사용될 수 있습니다.

#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;
}

출력은 다음과 같습니다.

Derived1 method
Derived2 method


답변