什么是奇怪的重复模板模式(CRTP)?

时间:2010-11-13 15:30:08

标签: c++ templates c++-faq crtp

如果没有参考书籍,任何人都可以通过代码示例为CRTP提供一个很好的解释吗?

4 个答案:

答案 0 :(得分:250)

简而言之,CRTP是指A类具有基类,它是A类本身的模板特化。 E.g。

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减少) )。尝试做它作为一个练习!

又一个有用的例子,对于提升(我不确定他们是如何实现它的,但是CRTP也会这样做)。 想象一下,您只想提供运营商&lt;对于你的类,但自动运算符==为他们!

你可以这样做:

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, isnit 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明确提供operator ==?但你拥有它!你可以写

int main()
{
    Apple a1;
    Apple a2; 

    a1.size = 10;
    a2.size = 10;
    if(a1 == a2) //the compiler won't complain! 
    {
    }
}

如果你只是为Apple编写operator ==,你可能会写得更少,但想象一下Equality模板不仅会提供==但是&gt;,&gt; =,&lt; =等等。你可以使用多个类的这些定义,重用代码!

CRTP是一件很棒的事情:) HTH

答案 1 :(得分:41)

在这里你可以看到一个很好的例子。如果使用虚方法,程序将知道在运行时执行什么。实现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);
    }
};

答案 2 :(得分:15)

CRTP is a technique to implement compile-time polymorphism. Here's a very simple example. In the below example, ProcessFoo() is working with Base class interface and Base::Foo invokes the derived object's foo() method, which is what you aim to do with virtual methods.

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

Output:

derived foo
AnotherDerived foo

答案 3 :(得分:5)

同样注意:

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