8

I have a set of classes that implement the same business methods. I plan to use CRTP instead of virtual dispatch due to performance reasons. But I'd like to keep the convenience of coding to a single interface that comes with inheritance and virtual methods.

Is it ok to have my specialised classes inherit from both a templated abstract class that would use CRTP to hold common code, and also inherit from a pure virtual class so I can then create instances of each type but have my client code depend only on the interface? Even better, how can I use CRTP to provide a single interface to client code while having multiple implementations?

ruipacheco
  • 15,025
  • 19
  • 82
  • 138
  • Well if you plan to refactor your code due to performance reasons abstract method is not a good idea... You use CRTP to avoid virtual dispatch that is why its common alternative name is static polymorphism.. Of course from syntactic point of view no one can stop you from doing this, but IMHO you need to think it through... – W.F. Dec 01 '16 at 12:19
  • The public methods wouldn't be in the interface, only in the specialisations. – ruipacheco Dec 01 '16 at 12:34

1 Answers1

10

Sure. You can use an approach like this, which is perfectly valid:

class Interface 
{
public:
    virtual void doSomething() = 0;
    //...
};

template<typename T>
class GeneralImpl : public Interface
{
public:

    void doSomething() override
    {
        auto someDetail = T::somethingStatic();
        //...
        static_cast<T*>(this)->someMember();
        //...
    }
}

class SpecificImpl : public GeneralImpl<SpecificImpl>
{
public:
    static int somethingStatic()
    {
        //...
    }

    void someMember()
    {
        //...
    }
};

int main()
{
    std::vector<Interface*> vec;
    SpecificImpl instance;

    //...

    vec.push_back(&instance);

    //...

    for(auto* inst : vec) {
        inst->doSomething();
    }

    //...
}
Smeeheey
  • 9,906
  • 23
  • 39
  • Would the runtime cost be the same as using pure virtual functions only? – ruipacheco Dec 01 '16 at 12:42
  • Yes, because templates have no runtime overhead at all. – Smeeheey Dec 01 '16 at 12:44
  • So looking at this code CRTP won't give me much when compared to using pure virtual functions only? I have the same performance penalty? – ruipacheco Dec 01 '16 at 12:53
  • 1
    Sorry, misunderstood your question. If you want to, you can make it so that the top level virtual function call (the `inst->doSomething()` in the `main` function in my example) is the only one that carries the virtual call overhead. The details of dispatching to the specific instance underneath that (so things like `static_cast(this)->someMember()` in the above) do not incur any such penalty due to their template nature. This is definitely more efficient than the abstract-only approach, where specific instance code is invoked via protected overridden virtual methods. – Smeeheey Dec 01 '16 at 13:03
  • 2
    So you have the best of both worlds: efficiency of templates for your low-level implementation, but still advantages of virtual polymorphism at the top level that allows, for example, the client to keep all polymorphic instances in a single container. – Smeeheey Dec 01 '16 at 13:04
  • This is exactly what I wanted to know. Thanks! – ruipacheco Dec 01 '16 at 13:05
  • 1
    @Smeeheey Coming here after a while, but I am not sure you have the best of both worlds. Indeed you still have the cost of virtual function call overhead when you call `doSomething` now. So isn't it simple to scrap CRTP altogether and use plain old polymorphism with virtual functions? – volatile Apr 01 '20 at 17:27