How to design around the limitation that templated

2019-01-19 19:46发布

问题:

I'm running into a design issue where (in C++) I'd like a templated member function (of a non-template class) to be virtual and am wondering if there is a good, elegant way around the issue.

The scenario goes, I have machines that process generic items. I use an abstract base class for machines with a virtual process(Item) function so that each machine can define their own unique processing method. The problem is that the items are also "generic" in that they expose certain interfaces for how they can be processed. For reasons (mainly for performance...no vtable overhead), I'd like to use compile-time polymorphism for these items. So that now each machine would have an interface like:

class Machine
{ public:
    template <typename T>
    virtual void process(T& item) = 0; 
};

However this is impossible in C++ as templated member functions cannot be virtual. certainly I can make the machine class templated on the Item type T but this adds more headaches for me in the larger design scheme and really no other part of the Machine class depends on Item...it's only an argument to the process() function.

Is there a better way around this or any suggestions for how to provide this kind of generic family of machines that process a family of generic items (where the items use compile-time polymorphism). Am I off the deep end in terms of my design.

Appreciate any suggestions

回答1:

Typically, double dispatch is used.

class Machine;
class Item {
public:
    virtual void bounce(Machine& mach);
};
class Machine {
public:
    template<typename T> void process(T& t);
    virtual void process(Item& i) {
        return i.bounce(*this);
    }
};
template<typename T> class CRTPItem {
public:
    virtual void bounce(Machine& mach) {
        return mach.process(*(T*)this);
    }
};
class ConcreteItem : public CRTPItem<ConcreteItem> {
public:
    // blah blah
};

In this case, you don't need virtual overhead for the whole ConcreteItem interface, and they don't need anything in common, just that one bounce function which is built for you automatically by inheriting from CRTPItem. This is only two vtable calls as opposed to the one you had originally, as opposed to needing a vtable call for all of Item's functions, and the interface can still retain all strong-typing that it would have if you could create virtual templates.