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

发布于 2024-12-07 16:16:43 字数 625 浏览 0 评论 0原文

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

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

但我想避免出现这样的情况。我正在考虑创建一个以 typeid 作为键的 std::map ,但是我会放什么作为值呢?

I have a class hierarchy where I want to introduce a method template that would behave like if it was virtual. For example a simple hierarchy:

class A {
  virtual ~A() {}

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

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

Then I create object B:

A *a = new B();

I know I can get the type stored in a by typeid(a). How can I call the correct B::method dynamically when I know the type? I could probably have a condition like:

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

But I would like to avoid having conditions like that. I was thinking about creating a std::map with typeid as a key, but what would I put as a value?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

¢好甜 2024-12-14 16:16:43

您可以使用“奇怪的重复模板模式”
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);

You can use the "Curiously Recurring Template Pattern"
http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

Using this pattern, the base class takes the derived class type as a template parameter, meaning that the base class can cast itself to the derived type in order to call functions in the derived class. It's a sort of compile time implementation of virtual functions, with the added benefit of not having to do a virtual function call.

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

It can then be used like this...

int one = 1;
A<B> *a = new B();
a->method(one);
此岸叶落 2024-12-14 16:16:43

有没有可以提取并虚拟化的通用代码?

class A {
  virtual ~A() {}

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

  virtual void DoSomeWork() {}
};

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

Is there any common code you could extract and make virtual?

class A {
  virtual ~A() {}

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

  virtual void DoSomeWork() {}
};

class B : public A {
  virtual void DoSomeWork() {}
};
夏末的微笑 2024-12-14 16:16:43

您可能知道,您不能拥有虚函数的模板,因为整个虚函数都是类类型的一部分,并且必须提前知道。这排除了任何简单的“任意覆盖”。

如果可以的话,您可以将模板参数作为类的一部分:

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) { }
  // ...
};

As you may know, you cannot have templates for virtual functions, since the entirety of the virtual functions is part of the class type and must be known in advance. That rules out any simple "arbitrary overriding".

If it's an option, you could make the template parameter part of the class:

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

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

A more involved approach might use some dispatcher object:

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) { }
  // ...
};
随波逐流 2024-12-14 16:16:43

哎呀。最初回答错误的问题 - 啊好吧,在另一个问题

经过一番思考,我认为这是经典的多方法要求,即基于多个参数的运行时类型。相比之下,通常的虚拟函数是单一调度(它们仅在this类型上调度)。

请参阅以下内容:

  • Andrei Alexandrescu 撰写了关于在“现代 C++ 设计”中使用泛型实现多种方法的文章(C++ 的开创性位?)
    • 第 11 章:“多方法” - 它实现了基本的多方法,使它们对数(使用有序类型列表),然后一直到恒定时间多方法。相当强大的东西!
  • 一篇 codeproject 文章 似乎就有这样的实现:
    • 不使用任何类型的类型转换(动态、静态、重新解释、const 或 C 风格)
    • 不使用 RTTI;
    • 不使用预处理器;
    • 强类型安全性;
    • 单独编译;
    • 多方法执行时间恒定;
    • 在多方法调用期间没有动态内存分配(通过 new 或 malloc);
    • 不使用非标准库;
    • 仅使用标准 C++ 功能。
  • C++ 开放方法编译器,Peter Pirkelbauer、Yuriy Solodkyy 和 Bjarne Stroustrup
  • Loki 库有 多重调度程序
  • 维基百科有一个很好的简单的文章和示例关于 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
        }
    }
}

Oops. Initially answered at the wrong question - ah well, at another question

After some thinking I recognized this as the classic multi-method requirement, i.e. a method that dispatches based on the runtime type of more than one parameter. Usual virtual functions are single dispatch in comparison (and they dispatch on the type of this only).

Refer to the following:

  • Andrei Alexandrescu has written (the seminal bits for C++?) on implementing multi-methods using generics in 'Modern C++ design'
    • Chapter 11: "Multimethods" - it implements basic multi-methods, making them logarithmic (using ordered typelists) and then going all the way to constant-time multi-methods. Quite powerful stuff !
  • A codeproject article that seems to have just such an implementation:
    • no use of type casts of any kind (dynamic, static, reinterpret, const or C-style)
    • no use of RTTI;
    • no use of preprocessor;
    • strong type safety;
    • separate compilation;
    • constant time of multimethod execution;
    • no dynamic memory allocation (via new or malloc) during multimethod call;
    • no use of nonstandard libraries;
    • only standard C++ features is used.
  • C++ Open Method Compiler, Peter Pirkelbauer, Yuriy Solodkyy, and Bjarne Stroustrup
  • The Loki Library has A MultipleDispatcher
  • Wikipedia has quite a nice simple write-up with examples on Multiple Dispatch in C++.

Here is the 'simple' approach from the wikipedia article for reference (the less simple approach scales better for larger number of derived types):

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文