Thread: Struggling to understand the benefit of CRTP

  1. #1
    Registered User
    Join Date
    Jan 2017
    Posts
    6

    Struggling to understand the benefit of CRTP

    I understand what CRTP is and how/why it works the way it does, but I'm struggling to under why I'd ever want to use it over alternatives.

    The most common reason I hear is that CRTP is used to implement static polymorphism in order to avoid the overhead of using vtables. But isn't it possible to implement static polymorphic behaviour even without virtual methods?

    The main reason I use virtual is when I want something like:
    Code:
    std::vector<Base*> v;
    DerivedA a;
    DerivedB b;
    v.push_back(&a);
    v.push_back(&b);
    for (auto obj_ptr : v) { obj_ptr->hello(); }
    I don't believe this can be achieved with CRTP because the type of an object needs to be known at compile time for CRTP to work. Also since the following two classes have no common ancestor, I can't use subtype polymorphism (I believe that's what it's called).
    Code:
    struct DerivedA : public Base<DerivedA> {};
    struct DerivedA : public Base<DerivedB> {};
    This is an example which I believe is similar to the ones commonly used in articles talking about CRTP.

    Code:
    #include <iostream>
    
    template <typename T>
    struct Base
    {
        void interface(int i)
        {
            static_cast<T*>(this)->implementation(i);
        }
    };
    
    struct Derived_A : public Base<Derived_A>
    {
        void implementation(int i)
        {
            std::cout << "Derived_A: " << i << "\n";
        }
    };
    
    struct Derived_B : public Base<Derived_B>
    {
        void implementation(int i)
        {
            std::cout << "Derived_B: " << i << "\n";
        }
    };
    
    template <typename T>
    void many_print(Base<T> &obj, int limit)
    {
        for (int i = 0; i < limit; i++)
        {
            obj.interface(i);
        }
    }
    
    struct Bad {};
    
    int main()
    {
        Derived_A a;
        a.interface(10);
        
        Derived_B b;
        b.interface(20);
        
        many_print(a, 10);
        many_print(b, 20);
        
        // Bad bad;
        // many_print(bad, 30);
        
        return 0;
    }
    But I can achieve this without CRTP and without vtables:

    Code:
    #include <iostream>
    
    struct Base
    {
        void interface(int i);
    };
    
    struct Derived_A : public Base
    {
        void interface(int i)
        {
            std::cout << "Derived_A: " << i << "\n";
        }
    };
    
    struct Derived_B : public Base
    {
        void interface(int i)
        {
            std::cout << "Derived_B: " << i << "\n";
        }
    };
    
    template <typename T>
    void many_print(T &obj, int limit)
    {
        for (int i = 0; i < limit; i++)
        {
            obj.interface(i);
        }
    }
    
    struct Bad {};
    
    int main()
    {
        Derived_A a;
        a.interface(10);
        
        Derived_B b;
        b.interface(20);
        
        many_print(a, 10);
        many_print(b, 20);
        
        // Bad bad;
        // many_print(bad, 30);
        
        return 0;
    }
    So what exactly can I do with CRTP that I can't do with classes that don't use vtables? I'd appreciate a straight to the point example please
    Last edited by sythical; 02-14-2018 at 06:51 AM.

  2. #2
    Registered User
    Join Date
    Jan 2017
    Posts
    6
    I came across a post on Stack Overflow (c++ - Confusion about CRTP static polymorphism - Stack Overflow) which finally helped me understand when CRTP is useful. It is useful when we want to call overriden methods from a non-overriden method.

    In the example below, I don't override say_hello in Derived. When say_hello is called on instance of derived, it calls the incorrect/unexpected version of hello (I'm guessing because the function to call is determined at compile time).

    Code:
    #include <iostream>
    
    struct Base
    {    
        void say_hello(const int n)
        {
            for (int i = 0; i < n; i++)
            {
                hello();
            }
        }
        
        void hello() // must use derived here for this to work as intended
        {
            std::cout << "Base says hello\n";
        }
    };
    
    struct Derived : public Base
    {
        void hello()
        {
            std::cout << "Derived says hello\n";
        }
    };
    
    int main()
    {
        Derived d;
        d.say_hello(10);
        
        return 0;
    }
    To fix this, we can make hello virtual so that the compiler implements a vtable in order to call the correct method at compile time. However, we can also use CRTP if we're not interested in other functionality that virtual brings.

    Code:
    #include <iostream>
    
    template <typename Derived>
    struct Base
    {    
        void say_hello(const int n)
        {
            for (int i = 0; i < n; i++)
            {
                static_cast<Derived*>(this)->hello();
            }
        }
        
        void hello()
        {
            std::cout << "Base says hello\n";
        }
    };
    
    struct Derived : public Base<Derived>
    {
        void hello()
        {
            std::cout << "Derived says hello\n";
        }
    };
    
    int main()
    {
        Derived d;
        d.say_hello(10);
        
        return 0;
    }
    Last edited by sythical; 02-15-2018 at 08:28 AM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Struggling to understand C, and where I am going wrong.
    By DARK_STALKER in forum C Programming
    Replies: 1
    Last Post: 11-29-2012, 10:44 AM
  2. CRTP how to pass type
    By FrEEzE2046 in forum C++ Programming
    Replies: 1
    Last Post: 12-08-2009, 09:26 AM
  3. Is This Legal C++? [CRTP]
    By Dante Shamest in forum C++ Programming
    Replies: 8
    Last Post: 05-07-2005, 01:23 PM
  4. what is benefit of using window NT?
    By Mane in forum A Brief History of Cprogramming.com
    Replies: 8
    Last Post: 03-29-2002, 08:13 AM
  5. For the Benefit of Mr. Kite
    By Aran in forum A Brief History of Cprogramming.com
    Replies: 4
    Last Post: 10-25-2001, 06:24 AM

Tags for this Thread