2011-09-30 63 views
4

我有一个类层次结构,我想介绍一个方法模板,它的行为就像虚拟一样。例如一个简单的层次结构:如何模拟方法模板的虚拟性

class A { 
    virtual ~A() {} 

    template<typename T> 
    void method(T &t) {} 
}; 

class B : public A { 
    template<typename T> 
    void method(T &t) {} 
}; 

然后,我创建对象B:

A *a = new B(); 

我知道我可以通过typeid(a)存储在a类型。当我知道该类型时,如何动态调用正确的B::method?我可能有这样的情况:

if(typeid(*a)==typeid(B)) 
    static_cast<B*>(a)->method(params); 

但我想,以避免类似的情况。我正在考虑创建一个std::map作为关键字typeid,但是我会把它作为一个价值?

回答

1

如您所知,由于虚拟函数的整体属于类类型的一部分并且必须事先知道,因此不能为虚拟函数提供模板。这排除了任何简单的“任意重写”。

如果它是一个选项,你可以使类的模板参数部分:

template <typename T> class A 
{ 
protected: 
    virtual void method(T &); 
}; 

template <typename T> class B : public A<T> 
{ 
    virtual void method(T &); // overrides 
}; 

一个更复杂的方法可能使用一些调度对象:

struct BaseDispatcher 
{ 
    virtual ~BaseDispatcher() { } 
    template <typename T> void call(T & t) { dynamic_cast<void*>(this)->method(t); } 
}; 
struct ConcreteDispatcher : BaseDispatcher 
{ 
    template <typename T> void method(T &); 
}; 

class A 
{ 
public: 
    explicit A(BaseDispatcher * p = 0) : p_disp(p == 0 ? new BaseDispatcher : p) { } 
    virtual ~A() { delete p_disp; }; 
private: 
    BaseDispatcher * p_disp; 
    template <typename T> void method(T & t) { p_disp->call(t); } 
}; 

class B : public A 
{ 
public: 
    B() : A(new ConcreteDispatcher) { } 
    // ... 
}; 
+0

我不能那样做。我真的需要一个方法模板。 –

+0

@JurajBlaho:我又增加了一个想法。 –

+0

@JurajBlaho:恩,[使成员模板虚拟只是不能在C + +](http://stackoverflow.com/questions/2354210/can-a-member-function-template-be-virtual/2354671#2354671) ,所以你将不得不重新考虑你的方法,并对语言的限制做出一些让步。 – sbi

2

是否有任何共同的代码,你可以提取和虚拟?

class A { 
    virtual ~A() {} 

    template<typename T> 
    void method(T &t) 
    { 
     ... 
     DoSomeWork(); 
     ... 
    } 

    virtual void DoSomeWork() {} 
}; 

class B : public A { 
    virtual void DoSomeWork() {} 
}; 
+0

纯粹的常识驱动!这有帮助,谢谢! – Arun

6

您可以使用“奇异递归模板模式” http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

地使用这种模式,基类在派生类类型作为模板参数,这意味着基类可以施展自己的派生类型以便调用派生类中的函数。这是一种编译时虚拟函数的实现,不需要做虚拟函数调用。

template<typename DERIVED_TYPE> 
class A { 
public: 
    virtual ~A() {} 

    template<typename T> 
    void method(T &t) { static_cast<DERIVED_TYPE &>(*this).methodImpl<T>(t); } 
}; 

class B : public A<B> 
{ 
friend class A<B>; 

public: 
    virtual ~B() {} 

private: 
    template<typename T> 
    void methodImpl(T &t) {} 
}; 

然后,它可以像这样使用...

int one = 1; 
A<B> *a = new B(); 
a->method(one); 
+1

在这种情况下不会有基类,所以你不能存储这些类。在一个向量中,因为A类是一个模板。但是,您可以从一个常见的ABase继承,但在这种情况下,您无法从Base中访问该方法。 –

+0

@工业抗抑郁药是的,这种方法有明显的缺点,例如不能在容器中存储不同的派生类型,但这仍然是一个有用的设计模式。 – Alan

0

糟糕。最初回答at the wrong question - 不错啊,在另一个问题

经过一番思考我认识到了这个经典的多方法要求,即一个方法,它能将基于多个参数的运行时类型 。通常的虚拟功能是single dispatch比较(和他们派遣的类型this只)。

请参考以下:

  • 安德烈Alexandrescu的写上实施使用泛型在 '现代C++设计'
    • Chapter 11: "Multimethods"多方法(吗?C++精液比特) - 它实现了基本的多方法,使它们成为对数(使用有序的类型列表),然后一直到恒定时间的多方法。相当强大的东西!
  • 一个codeproject article,似乎刚才这样的实现:
    • 没有用型投下任何种类的(动态,静态,重新诠释,const或C型)
    • 没有用的RTTI ;
    • 不使用预处理器;
    • 强型安全;
    • 单独编译;
    • 多方法执行的恒定时间;
    • 在multimethod调用期间没有动态内存分配(通过new或malloc);
    • 不使用非标准库;
    • 只使用标准的C++功能。
  • C++ Open Method Compiler,彼得Pirkelbauer,尤里Solodkyy和Bjarne的Stroustrup的
  • 洛基图书馆有A MultipleDispatcher
  • 维基百科有与C++在多分派的例子相当不错simple write-up

这里是从维基百科文章供参考 '简单' 的方法(在以下简单的方法扩展为较大数量的派生类型的更好):

// Example using run time type comparison via dynamic_cast 

struct Thing { 
    virtual void collideWith(Thing& other) = 0; 
} 

struct Asteroid : Thing { 
    void collideWith(Thing& other) { 
     // dynamic_cast to a pointer type returns NULL if the cast fails 
     // (dynamic_cast to a reference type would throw an exception on failure) 
     if (Asteroid* asteroid = dynamic_cast<Asteroid*>(&other)) { 
      // handle Asteroid-Asteroid collision 
     } else if (Spaceship* spaceship = dynamic_cast<Spaceship*>(&other)) { 
      // handle Asteroid-Spaceship collision 
     } else { 
      // default collision handling here 
     } 
    } 
} 

struct Spaceship : Thing { 
    void collideWith(Thing& other) { 
     if (Asteroid* asteroid = dynamic_cast<Asteroid*>(&other)) { 
      // handle Spaceship-Asteroid collision 
     } else if (Spaceship* spaceship = dynamic_cast<Spaceship*>(&other)) { 
      // handle Spaceship-Spaceship collision 
     } else { 
      // default collision handling here 
     } 
    } 
}