如何模拟方法模板的虚拟性

时间:2011-09-30 12:28:27

标签: c++ templates polymorphism

我有一个类层次结构,我想引入一个方法模板,就像它是虚拟的一样。例如,一个简单的层次结构:

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

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

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

但我想避免出现这样的情况。我正在考虑创建一个以std::map作为关键字的typeid,但我会将其作为一个值?

5 个答案:

答案 0 :(得分: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 :(得分:2)

您是否可以提取和制作虚拟的常用代码?

class A {
  virtual ~A() {}

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

  virtual void DoSomeWork() {}
};

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

答案 2 :(得分: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) { }
  // ...
};

答案 3 :(得分:0)

答案 4 :(得分:0)

<子> 糟糕即可。最初回答at the wrong question - 好吧,在另一个问题上

经过一番思考后,我认为这是经典的多方法要求,即根据多个参数的运行时类型进行调度的方法。通常的虚拟函数比较single dispatch(并且它们仅针对 this 的类型进行调度。)

请参阅以下内容:

  • Andrei Alexandrescu在“现代C ++设计”中使用泛型实现多方法的文章(C ++的开创性内容?)
    • Chapter 11: "Multimethods" - 它实现了基本的多方法,使它们成为对数(使用有序的类型列表),然后一直到恒定时间的多方法。相当强大的东西!
  • 似乎只有这样一个实现的codeproject article
    • 不使用任何类型的类型转换(动态,静态,重新解释,const或C风格)
    • 不使用RTTI;
    • 不使用预处理器;
    • 强力型安全;
    • 单独编译;
    • 多方法执行的恒定时间;
    • 在multimethod调用期间没有动态内存分配(通过new或malloc);
    • 不使用非标准库;
    • 仅使用标准C ++功能。
  • C++ Open Method Compiler,Peter Pirkelbauer,Yuriy Solodkyy和Bjarne Stroustrup
  • Loki图书馆有A MultipleDispatcher
  • 维基百科有一个很好的simple write-up,其中包含C ++中的多个调度的示例。

以下是维基百科文章中的“简单”方法供参考(对于大量派生类型,不太简单的方法可以更好地扩展):

<子>

// 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
        }
    }
}