调用派生类的模板函数 [英] calling a template function of a derived class

查看:61
本文介绍了调用派生类的模板函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在C ++中,我遇到了一个问题,即在拥有指向基类的指针的同时调用派生类的函数.

I'm having a problem in C++ with calling a function of a derived class while having a pointer to the base class.

一些答案使我想到了 CRTP

但是我的意思是我需要有一个指向"Base *"类而不是"Base *"的指针,因为我不知道当前正在处理的类型(当前实例是从某种工厂创建的)

but my point is that I need to have a pointer to the "Base*" class not "Base*" because I'm unaware of the type currently being handled (The current instance is created from some sort of a factory).

class Base 
{
..
template<typename T>
func (T arg) { ... };
};

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


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

用法:

int main()
{
   Base* BasePtr = new Derived1();

   // The expected function to be called is Derived1::func<int>()
   BasePtr->func<int>();

   return 0; // :)
}

我无法使func虚拟化,因为该语言不支持虚拟模板功能.

I can't make func virtual because the language does not support virtual template function.

仅在只有类具有模板参数的情况下才允许,而在其中的函数具有模板参数的情况下则不允许.

It is only allowed if only the class is have template arguments but not if the function within it has template arguments.

我看到在Boost.Serialization中解决了一个类似的问题,但无法理解解决方案.

I have seen a similar problem resolved within Boost.Serialization but couldn't understand the solution.

谢谢

科比·梅尔

推荐答案

现有的两个解决方案将动态多态性替换为静态多态性.如果没有更多关于手头问题的细节,就不可能知道这是否是一种有效的方法,因为它基本上破坏了多态层次:对于CRTP,没有单个基类,而是一个基类.您不能将 Derived1 Derived2 的对象放在不相关的同一个容器中...如果您只需要共享代码,但不是共享代码,这是一个很好的解决方案如果需要动态多态性.看一看访客"模式,并为类似的问题进行两次调度.

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.

如果需要动态多态性,则可以尝试实现双重调度(这很痛苦,但是如果层次结构足够小,则是可行的.基本上创建两个不同的层次结构,一个层次结构以 Base 为根,另一个层次结构为根植于 Base 的层次结构将具有虚拟方法 apply ,第二层次结构将具有针对每个类型的虚函数.第一层级:

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); } 
};

我使用的名称在Visitor模式中很常见,并且这种方法与该模式非常相似(我不敢称之为Visitor模式,但是这种方法是相似的,所以我只是借用了命名约定)

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).

用户代码类似于:

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
}

可以通过将函数的参数从引用更改为const-references(如果可能)来释放在声明手之前声明 i c 的要求.实际的魔术在于,在[1]中,C ++单一调度机制将调度对 Derived1 :: apply 的调用,因为这是由 BasePtr 指向的对象的动态类型..届时,它将以自身为参数调用 Visitor :: visit(Derived1&).它将再次通过单一调度机制调度到 FooCaller< int> :: visit(Derived1&),并且此时两个对象都已解析为其静态类型.当 FooCaller< int> :: visit 调用 call_foo 时,参数 U 被推导为 Derived1 . Derived1 :: foo 该参数被推导为 int ,最终调用了 Derived1 :: foo< int> ...循环和间接寻址...

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...

根据您的特定用例,这可能太复杂(如果像CRTP这样的静态多态性可以工作)或太难维护(如果层次结构很大:对于 Base 层次结构中的每个新元素您将必须在 Visitor 的层次结构中更新所有类型,因此,如果可以避免这种复杂性,那就太完美了.不过,在某些情况下,您需要这样做.

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.

还要注意,这在C ++中是很不常见的,并且如果您解释了眼前的实际问题,可能会有更好的简单解决方案,您所说的是对原始问题的解决方案的要求:动态分派给模板.

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.

这篇关于调用派生类的模板函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆