Sin hacer referencia a un libro, ¿puede alguien por favor proporcionar una buena explicación para CRTP con un ejemplo de código?

Leer CRTP preguntas en MODO: stackoverflow.com/questions/tagged/crtp. Que podría dar alguna idea.
Si lo hace, va a encontrar a su propia pregunta. Y que sería curiosamente recurrente. 🙂
Por CIERTO, a mí me parece que el término debe ser «curiosamente recursing». Soy la incomprensión del significado?
Craig: yo creo que son; es «curiosamente recurrente» en el sentido de que se encontró a los cultivos en varios contextos.

OriginalEl autor Alok Save | 2010-11-13

5 Comentarios

  1. 237

    En resumen, CRTP es cuando una clase tiene una clase base que es una especialización de plantilla para que la clase en sí. E. g.

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

    Se es curiosamente recurrente, ¿no? 🙂

    Ahora, ¿qué significa esto? En realidad, esto le da a la X de la plantilla de la capacidad de ser una clase base para su especialización.

    Por ejemplo, usted podría hacer un genérico de clase singleton (versión simplificada) como este

    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;

    Ahora, con el fin de hacer una arbitraria de la clase singleton usted debe hacer esto

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

    Así que usted puede ver? El singleton plantilla supone que su especialización para cualquier tipo X se hereda de singleton<X> y por lo tanto va a tener a todos sus(public, protected) miembros accesibles, incluyendo la GetInstance! Hay otros usos de CRTP. Por ejemplo, si desea contar todas las instancias que existen en la actualidad para su clase, pero desea encapsular esta lógica en una plantilla independiente (la idea de una clase concreta es bastante simple – tiene una variable estática, el incremento de la ctors, decremento en el dtors). Trate de hacerlo como un ejercicio!

    Otro ejemplo útil, para el impulso(no estoy seguro de cómo se han implementado, pero CRTP que también lo hacen).
    Imagine que usted desea proporcionar único operador < para sus clases, pero automáticamente el operador == para ellos!

    que usted podría hacer algo como esto:

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

    Ahora usted puede utilizar esta

    struct Apple:public Equality<Apple> 
    {
        int size;
    };
    
    bool operator < (Apple const & a1, Apple const& a2)
    {
        return a1.size < a2.size;
    }

    ahora, no ha proporcionado de forma explícita el operador == para apple? Pero lo tienen! Usted puede escribir

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

    Esto podría parecer que iba a escribir menos si usted acaba de escribir el operador == para Apple, pero imagino que la Igualdad de la plantilla no sólo serviría == pero >, >=, <= etc. Y usted puede utilizar estas definiciones para múltiples clases, reutilizando el código!

    CRTP es una cosa maravillosa 🙂 HTH

    Honestamente, esta respuesta ha sido útil en la comprensión del concepto de CRTP aunque singleton no sería la mejor opción de un ejemplo todavía la respuesta hizo servir al propósito.Puesto que usted votada abajo estoy esperando que vengan con una mejor respuesta/ejemplo & mantener la ventana abierta para que no este marcado como aceptado respuesta.
    + 1 la mejor explicación que he visto nunca
    Este post no aboga por singleton como una buena programación patrón.se utiliza simplemente como una ilustración que puede ser que comúnmente se entiende.imo la-1 es injustificado
    invierte su teléfono celular porque es demasiado duro para el uso de ella? :)
    La respuesta explica CRTP en una forma que pueda ser entendido claramente, es una bonita respuesta, gracias por tan bonita respuesta.

    OriginalEl autor Armen Tsirunyan

  2. 37

    Aquí puedes ver un gran ejemplo. Si utiliza el método virtual, el programa va a saber lo que se ejecuten en tiempo de ejecución. La aplicación de CRTP el compilador es el que decide en tiempo de compilación!!! Este es un gran rendimiento!

    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);
        }
    };
    No podía hacer esto mediante la definición de virtual void write(const char* str) const = 0;? Aunque para ser justos, esta técnica parece super útil cuando write está haciendo el trabajo de otros.
    El uso de un método virtual puro que se está resolviendo la herencia en tiempo de ejecución en lugar de tiempo de compilación. CRTP se utiliza para resolver este en tiempo de compilación, por lo que la ejecución será más rápida.

    OriginalEl autor GutiMac

  3. 9

    CRTP es una técnica para implementar en tiempo de compilación polimorfismo. He aquí un ejemplo muy sencillo. En el ejemplo de abajo, ProcessFoo() está trabajando con Base interfaz de clase y Base::Foo invoca el objeto derivado de la foo() método, que es lo que pretenden hacer con los métodos virtuales.

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

    De salida:

    derived foo
    AnotherDerived foo
    También podría ser la pena en este ejemplo, para añadir un ejemplo de cómo implementar un defecto foo() en la clase Base, que será llamada si no Derivados ha puesto en práctica. También conocido como cambio de foo en la Base de algún otro nombre(por ejemplo, caller()), agregar una nueva función foo() de la Base de que cout la «Base». Luego de la llamada caller() dentro de ProcessFoo
    Este ejemplo es más para ilustrar un virtual pura función de la clase base, es decir, exigir que foo() es implementado por la clase derivada.
    Este es mi favorito de respuesta, ya que también muestra por qué este patrón es útil con el ProcessFoo() función.

    OriginalEl autor blueskin

  4. 6

    Sólo como nota:

    CRTP podría ser utilizada para implementar el polimorfismo estático(que como polimorfismo dinámico pero sin función virtual puntero de la tabla).

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

    La salida sería :

    Derived1 method
    Derived2 method
    lo siento mi mal, static_cast se ocupa del cambio. Si desea ver el caso de esquina de todos modos aunque no causa el error ver aquí: ideone.com/LPkktf
    Mal ejemplo. Este código podría ser hecho sin vtables sin usar CRTP. Lo vtables proporciona realmente es el uso de la clase base (puntero o referencia) para llamar derivados de los métodos. Usted debe mostrar cómo se hace con CRTP aquí.
    En tu ejemplo, Base<>::method () ni siquiera se llama, ni tampoco se usa polimorfismo en cualquier lugar.
    de acuerdo con @MikeMB ‘s nota, usted debe llamar a methodImpl en el method de Base y en las clases derivadas nombre methodImpl en lugar de method
    si utiliza un método similar, (), a continuación, sus enlazado estáticamente y no necesita la clase base común. Porque de todos modos no se podía utilizar polymorphically a través de la base de la clase puntero o ref. Así, el código debe tener este aspecto: #include <iostream> plantilla <typename T> struct Escritor { void escribir() { static_cast<*>(este)->writeImpl(); } }; struct Derived1 : público Escritor<Derived1> { void writeImpl() { std::cout << «D1»; } }; struct Derived2 : público Escritor<Derived2> { void writeImpl() { std::cout << «DER2»; } };

    OriginalEl autor Jichao

  5. 4

    Esto no es una respuesta directa, sino más bien un ejemplo de cómo CRTP puede ser útil.


    Un buen ejemplo concreto de CRTP es std::enable_shared_from_this de C++11:

    [util.smartptr.enab]/1

    Una clase T puede heredar de enable_­shared_­from_­this<T> para heredar la shared_­from_­this funciones miembro que obtener un shared_­ptr instancia apunta a *this.

    Que es, la herencia de std::enable_shared_from_this hace posible la obtención de un compartida (o débil) puntero a la instancia sin acceso a ella (por ejemplo, de un miembro de la función en la que sólo se sabe acerca de *this).

    Es útil cuando necesitas dar un std::shared_ptr pero sólo tiene acceso a *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));
        }
    };

    La razón por la que no puede pasar this directamente en lugar de shared_from_this() es que se rompería la propiedad mecanismo:

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

    OriginalEl autor Mário Feroldi

Dejar respuesta

Please enter your comment!
Please enter your name here