calling a template function of a derived class

牧云@^-^@ 提交于 2019-12-03 03:44:18
David Rodríguez - dribeas

The two existing solutions trade dynamic polymorphism for static polymorphism. Without more details on the problem at hand, it is not possible to know whether that is a valid approach or not, as it basically breaks the polymorphic hierarchy: with CRTP there is no single base class, but rather a family of them. You cannot hold objects of Derived1 and Derived2 in the same container as they are unrelated... It is a fine solution if all you need is to share the code, but not if you need dynamic polymorphism. Take a look at the Visitor pattern and at double-dispatch for similar problems.

If you need dynamic polymorphism, you could try to implement double dispatch (it is a pain, but feasible if the hierarchy is small enough. Basically create two different hierarchies, one rooted at Base and another that serves as some short of manual dispatcher. The hierarchy rooted at Base will have a virtual method apply, and the second hierarchy will have virtual functions for each one of the types in the first hierarchy:

class Base;
class Derived1;  // inherits from Base, implements Visitor
class Derived2;  // inherits from either Base or Derived2
struct Visitor {
   virtual void visit( Base& ) = 0;     // manually unrolled for all types
   virtual void visit( Derived1& ) = 0;
   virtual void visit( Derived2& ) = 0;
};
struct Base {
   virtual void apply( Visitor& v ) {   // manually replicate this in Derived1, 2
      v.visit( *this );
   }
   template <typename T> void foo(T);   // implement 
};

template <typename T>
struct FooCaller : Visitor {
    T& ref_value;
    FooCaller( T& v ) : ref_value(v) {}
    template <typename U> void call_foo( U& o ) {
       o.foo(ref_value);
    }
    virtual void visit( Base & b )      { call_foo(b); }
    virtual void visit( Derived1 & d1 ) { call_foo(d1); }
    virtual void visit( Derived2 & d2 ) { call_foo(d2); } 
};

The names I have used are common in the Visitor pattern, and this approach is quite similar to that pattern (I don't dare call it the Visitor pattern, but the approach is similar, so I just borrowed the naming convention).

User code would be similar to:

int main()                     // main returns int, not void!!!
{
   Base* BasePtr = new Derived1();
   int i = 5;
   FooCaller<int> c(i)
   BasePtr->apply(c);          // [1] magic happens here
}

The requirement of declaring i and c before hand can be released by changing (if possible) the arguments to the functions from references to const-references. The actual magic is that in [1] the C++ single dispatch mechanism sill dispatch the call to Derived1::apply, since that is the dynamic type of the object pointed by BasePtr. At that point it will call Visitor::visit( Derived1& ) with itself as the argument. That will again be dispatched through the single dispatch mechanism to FooCaller<int>::visit( Derived1& ), and at that point both objects have been resolved to their static types. When FooCaller<int>::visit calls call_foo the argument U is deduced to be Derived1, when it calls Derived1::foo the argument is deduced to be int and it ends up calling Derived1::foo<int>... though a couple of loops and indirections...

Depending on your particular use case this might be too complex (if static polymorphism like CRTP would work) or too hard to maintain (if the hierarchy is big: for each new element in the Base hierarchy you will have to update all types in the hierarchy of the Visitor), so if you can avoid this complexity, perfect. In some cases, though, you need this.

Also note that this is the most complex fully dynamic solution, there are other options in between, depending on what is it that you need to be runtime polymorphism... It might be the case that your hierarchy models a visitor of shorts, and that you only need to manually unroll the different virtual functions that will dispatch to the template internally, in which case half of the above complexity will be gone.

Also note that this is quite unusual in C++, and that if you explain the actual problem at hand, there might be better simpler solutions, what you have stated are the requirements of your solution to the original problem: dynamically dispatch to a template.

Implement Curiously recurring template pattern(CTRP).

Illustration:

template<typename D>
class Base 
{
public:
    template<typename T>
    void func (T arg) 
    {
         static_cast<D*>(this)->func(arg);
    }
};

class Derived1 : public Base<Derived1>
{
public:
    template<typename T>
    void func (T arg) { /*...*/ }
};


Base<Derived1> *basePtr = new Base<Derived1>();
basePtr->func(100);
Alok Save

Check this out, it will help you implement the CRTP.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!