Curiously recurring template pattern (CRTP) - GeeksforGeeks

20 Sep, 2021
Background: It is recommended to refer Virtual Functions and Runtime Polymorphism as a prerequisite of this. Below is an example program to demonstrate run time polymorphism. 
CPP
// A simple C++ program to demonstrate run-time// polymorphism#include <chrono>#include <iostream>using namespace std; typedef std::chrono::high_resolution_clock Clock; // To store dimensions of an imageclass Dimension {public:    Dimension(int _X, int _Y)    {        mX = _X;        mY = _Y;    } private:    int mX, mY;}; // Base class for all image typesclass Image {public:    virtual void Draw() = 0;    virtual Dimension GetDimensionInPixels() = 0; protected:    int dimensionX;    int dimensionY;}; // For Tiff Imagesclass TiffImage : public Image {public:    void Draw() {}    Dimension GetDimensionInPixels()    {        return Dimension(dimensionX, dimensionY);    }}; // There can be more derived classes like PngImage,// BitmapImage, etc // Driver code that calls virtual functionint main(){    // An image type    Image* pImage = new TiffImage;     // Store time before virtual function calls    auto then = Clock::now();     // Call Draw 1000 times to make sure performance    // is visible    for (int i = 0; i < 1000; ++i)        pImage->Draw();     // Store time after virtual function calls    auto now = Clock::now();     cout << "Time taken: "         << std::chrono::duration_cast<std::chrono::nanoseconds>(now - then).count()         << " nanoseconds" << endl;     return 0;}
Output : 
Time taken: 2613 nanoseconds
See this for above result.When a method is declared virtual, compiler secretly does two things for us: 
Defines a VPtr in first 4 bytes of the class objectInserts code in constructor to initialize VPtr to point to the VTable
Defines a VPtr in first 4 bytes of the class object
Inserts code in constructor to initialize VPtr to point to the VTable
What are VTable and VPtr? When a method is declared virtual in a class, compiler creates a virtual table (aka VTable) and stores addresses of virtual methods in that table. A virtual pointer (aka VPtr) is then created and initialized to point to that VTable. A VTable is shared across all the instances of the class, i.e. compiler creates only one instance of VTable to be shared across all the objects of a class. Each instance of the class has its own version of VPtr. If we print the size of a class object containing at least one virtual method, the output will be sizeof(class data) + sizeof(VPtr). Since address of virtual method is stored in VTable, VPtr can be manipulated to make calls to those virtual methods thereby violating principles of encapsulation. See below example:  
CPP
// A C++ program to demonstrate that we can directly// manipulate VPtr. Note that this program is based// on the assumption that compiler store vPtr in a// specific way to achieve run-time polymorphism.#include <iostream>using namespace std; #pragma pack(1) // A base class with virtual function foo()class CBase {public:    virtual void foo() noexcept    {        cout << "CBase::Foo() called" << endl;    } protected:    int mData;}; // A derived class with its own implementation// of foo()class CDerived : public CBase {public:    void foo() noexcept    {        cout << "CDerived::Foo() called" << endl;    } private:    char cChar;}; // Driver codeint main(){    // A base type pointer pointing to derived    CBase* pBase = new CDerived;     // Accessing vPtr    int* pVPtr = *(int**)pBase;     // Calling virtual method    ((void (*)())pVPtr[0])();     // Changing vPtr    delete pBase;    pBase = new CBase;    pVPtr = *(int**)pBase;     // Calls method for new base object    ((void (*)())pVPtr[0])();     return 0;}
Output : 
CDerived::Foo() called
CBase::Foo() called 
We are able to access vPtr and able to make calls to virtual methods through it. The memory representation of objects is explained here.Is it wise to use virtual method? As it can be seen, through base class pointer, call to derived class method is being dispatched. Everything seems to be working fine. Then what is the problem? If a virtual routine is called many times (order of hundreds of thousands), it drops the performance of system, reason being each time the routine is called, its address needs to be resolved by looking through VTable using VPtr. Extra indirection (pointer dereference) for each call to a virtual method makes accessing VTable a costly operation and it is better to avoid it as much as we can. Curiously Recurring Template Pattern (CRTP) Usage of VPtr and VTable can be avoided altogether through Curiously Recurring Template Pattern (CRTP). CRTP is a design pattern in C++ in which a class X derives from a class template instantiation using X itself as template argument. More generally it is known as F-bound polymorphism.  
CPP
// Image program (similar to above) to demonstrate// working of CRTP#include <chrono>#include <iostream>using namespace std; typedef std::chrono::high_resolution_clock Clock; // To store dimensions of an imageclass Dimension {public:    Dimension(int _X, int _Y)    {        mX = _X;        mY = _Y;    } private:    int mX, mY;}; // Base class for all image types. The template// parameter T is used to know type of derived// class pointed by pointer.template <class T>class Image {public:    void Draw()    {        // Dispatch call to exact type        static_cast<T*>(this)->Draw();    }    Dimension GetDimensionInPixels()    {        // Dispatch call to exact type        static_cast<T*>(this)->GetDimensionInPixels();    } protected:    int dimensionX, dimensionY;}; // For Tiff Imagesclass TiffImage : public Image<TiffImage> {public:    void Draw()    {        // Uncomment this to check method dispatch        // cout << "TiffImage::Draw() called" << endl;    }    Dimension GetDimensionInPixels()    {        return Dimension(dimensionX, dimensionY);    }}; // There can be more derived classes like PngImage,// BitmapImage, etc // Driver codeint main(){    // An Image type pointer pointing to Tiffimage    Image<TiffImage>* pImage = new TiffImage;     // Store time before virtual function calls    auto then = Clock::now();     // Call Draw 1000 times to make sure performance    // is visible    for (int i = 0; i < 1000; ++i)        pImage->Draw();     // Store time after virtual function calls    auto now = Clock::now();     cout << "Time taken: "         << std::chrono::duration_cast<std::chrono::nanoseconds>(now - then).count()         << " nanoseconds" << endl;     return 0;}
Output :
Time taken: 732 nanoseconds
See this for above result.Virtual method vs CRTP benchmark The time taken while using virtual method was 2613 nanoseconds. This (small) performance gain from CRTP is because the use of a VTable dispatch has been circumvented. Please note that the performance depends on a lot of factors like compiler used, operations performed by virtual methods. Performance numbers might differ in different runs, but (small) performance gain is expected from CRTP. Note: If we print size of class in CRTP, it can bee seen that VPtr no longer reserves 4 bytes of memory. 
 
cout << sizeof(Image) << endl; 
Another use case of CRTP is, when it’s required to access the derived class object in the base class member functions then will have to use CRTP. 
CPP
#include <iostream>#include <typeinfo>using namespace std; template <typename DerivedT> class Base {public:    int accessDerivedData() // Parsing json object    {        // this will call the respective derived class object.        auto derived = static_cast<DerivedT*>(this);         // some generic parsing logic for any json object        // then call derived objects to set parsed values        derived->implementation();        derived->display();    }}; class Derived1 : public Base<Derived1> // jsonMessage1{public:    int data1;    Derived1() { cout << "Derived1 constr" << endl; }    void display()    {        cout << " data1:" << data1 << endl;    }    void implementation()    {        this->data1 = 8900;    }}; class Derived2 : public Base<Derived2> // jsonMessage2{public:    int data2;    Derived2() { cout << "Derived2 constr" << endl; }    void display()    {        cout << " data2:" << data2 << endl;    }    void implementation()    {        this->data2 = 898;    }}; int main(){    auto obj1 = new Derived1;    obj1->accessDerivedData();     auto obj2 = new Derived2;    obj2->accessDerivedData();}
Questions? Keep them coming. We would love to answer.Reference(s) https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern This article is contributed by Aashish Barnwal. If you like GeeksforGeeks and would like to contribute, you can also write an article and mail your article to review-team@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above 
RamsTime
gabaa406
Design Pattern
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
Comments
Old Comments
Command Pattern
Strategy Pattern | Set 1 (Introduction)
Template Method Design Pattern
State Design Pattern
Conceptual Model of the Unified Modeling Language (UML)
Visitor design pattern
Flyweight Design Pattern
Difference between Sequence Diagram and Activity Diagram
Difference Between Architectural Style, Architectural Patterns and Design Patterns