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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4173254/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 14:43:30  来源:igfitidea点击:

What is the curiously recurring template pattern (CRTP)?

c++templatesc++-faqcrtp

提问by Alok Save

Without referring to a book, can anyone please provide a good explanation for CRTPwith a code example?

不参考一本书,任何人都可以CRTP用代码示例提供一个很好的解释吗?

回答by Armen Tsirunyan

In short, CRTP is when a class Ahas a base class which is a template specialization for the class Aitself. E.g.

简而言之,CRTP 是指一个类A具有一个基类,该基类是该类A本身的模板特化。例如

template <class T> 
class X{...};
class A : public X<A> {...};

It iscuriously recurring, isn't it? :)

好奇地反复出现,不是吗?:)

Now, what does this give you? This actually gives the Xtemplate the ability to be a base class for its specializations.

现在,这给了你什么?这实际上使X模板能够成为其专业化的基类。

For example, you could make a generic singleton class (simplified version) like this

例如,您可以像这样创建一个通用的单例类(简化版)

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;

Now, in order to make an arbitrary class Aa singleton you should do this

现在,为了使任意类A成为单例,你应该这样做

class A: public Singleton<A>
{
   //Rest of functionality for class A
};

So you see? The singleton template assumes that its specialization for any type Xwill be inherited from singleton<X>and thus will have all its (public, protected) members accessible, including the GetInstance! There are other useful uses of CRTP. For example, if you want to count all instances that currently exist for your class, but want to encapsulate this logic in a separate template (the idea for a concrete class is quite simple - have a static variable, increment in ctors, decrement in dtors). Try to do it as an exercise!

所以你看?单例模板假定它对任何类型的特化X都将被继承singleton<X>,因此它的所有(公共的、受保护的)成员都可以访问,包括GetInstance!CRTP 还有其他有用的用途。例如,如果您想计算您的类当前存在的所有实例,但想将此逻辑封装在一个单独的模板中(具体类的想法非常简单 - 有一个静态变量,在 ctors 中递增,在 dtors 中递减)。尝试将其作为练习!

Yet another useful example, for Boost (I am not sure how they have implemented it, but CRTP will do too). Imagine you want to provide only operator <for your classes but automatically operator ==for them!

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

you could do it like this:

你可以这样做:

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

Now you can use it like this

现在你可以像这样使用它

struct Apple:public Equality<Apple> 
{
    int size;
};

bool operator < (Apple const & a1, Apple const& a2)
{
    return a1.size < a2.size;
}

Now, you haven't provided explicitly operator ==for Apple? But you have it! You can write

现在,您还没有==Apple? 但你拥有它!你可以写

int main()
{
    Apple a1;
    Apple a2; 

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

This could seem that you would write less if you just wrote operator ==for Apple, but imagine that the Equalitytemplate would provide not only ==but >, >=, <=etc. And you could use these definitions for multipleclasses, reusing the code!

这可能似乎是你会少写,如果你只是写操作==Apple,但想象Equality模板将不仅提供==,但是>>=<=等你可以使用这些定义为多个类,重用代码!

CRTP is a wonderful thing :) HTH

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

回答by GutiMac

Here you can see a great example. If you use virtual method the program will know what execute in runtime. Implementing CRTP the compiler is which decide in compile time!!! This is a great performance!

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

回答by blueskin

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

CRTP 是一种实现编译时多态的技术。这是一个非常简单的例子。在下面的示例中,ProcessFoo()正在使用Base类接口并Base::Foo调用派生对象的foo()方法,这就是您使用虚拟方法的目的。

http://coliru.stacked-crooked.com/a/2d27f1e09d567d0e

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

回答by Mário Feroldi

This is not a direct answer, but rather an example of how CRTPcan be useful.

这不是一个直接的答案,而是CRTP如何有用的一个例子。



A good concrete example of CRTPis std::enable_shared_from_thisfrom C++11:

的一个很好的具体的例子CRTPstd::enable_shared_from_this从C ++ 11:

[util.smartptr.enab]/1

A class Tcan inherit from enable_-shared_-from_-this<T>to inherit the shared_-from_-thismember functions that obtain a shared_-ptrinstance pointing to *this.

[util.smartptr.enab]/1

一个类T可以继承自来enable_-shared_-from_-this<T>继承shared_-from_-this获得shared_-ptr指向的实例的成员函数*this

That is, inheriting from std::enable_shared_from_thismakes it possible to get a shared (or weak) pointer to your instance without access to it (e.g. from a member function where you only know about *this).

也就是说,继承 fromstd::enable_shared_from_this可以在不访问它的情况下获取指向您的实例的共享(或弱)指针(例如,从您只知道 的成员函数*this)。

It's useful when you need to give a std::shared_ptrbut you only have access to *this:

当您需要提供 astd::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));
    }
};

The reason you can't just pass thisdirectly instead of shared_from_this()is that it would break the ownership mechanism:

您不能直接传递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);

回答by Jichao

Just as note:

正如注意:

CRTP could be used to implement static polymorphism(which like dynamic polymorphism but without virtual function pointer table).

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

The output would be :

输出将是:

Derived1 method
Derived2 method