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

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (13)

没有参考一本书,谁能给我一个很好的解释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的有用用途。例如,如果要计算当前为您的类存在的所有实例,但希望将此逻辑封装在单独的模板中(具体类的想法非常简单 - 有一个静态变量,ctors增量,dtors递减)。尝试做它作为练习!

还有一个有用的例子,对于提升(我不确定他们是如何实现它的,但是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, 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;
}

现在,你还没有提供明确的运营商==苹果?但你有它!你可以写

int main()
{
    Apple a1;
    Apple a2; 

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

如果你只为Apple写了operator ==,那么看起来你会写得更少,但想象一下Equality模板不仅会提供==,而且还会提供>,> =,<=等等。您可以将这些定义用于多个类,重用代码!

用户回答回答于

这里你可以看到一个很好的例子。

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

扫码关注云+社区