C++中的多重分派

2sbarzqh  于 2023-02-17  发布在  其他
关注(0)|答案(4)|浏览(147)

我试着理解什么是多重分派。我读了很多不同的文本,但我仍然不知道什么是多重分派,它是什么好。也许我错过的东西是一段代码使用多重分派。请,你能用C写一小段代码使用多个分派,这样我就可以看到它不能被正确编译/运行,因为C只有一个分派?我要看看有什么不同。谢谢。

wj8zmpe1

wj8zmpe11#

多分派是根据传递给函数调用的参数的运行时类型来选择要调用的函数版本的能力。
下面是一个在C++中无法正常工作的示例(未经测试):

class A { };
class B : public A { };
class C : public A { }

class Foo
{
  virtual void MyFn(A* arg1, A* arg2) { printf("A,A\n"); }
  virtual void MyFn(B* arg1, B* arg2) { printf("B,B\n"); }
  virtual void MyFn(C* arg1, B* arg2) { printf("C,B\n"); }
  virtual void MyFn(B* arg1, C* arg2) { printf("B,C\n"); }
  virtual void MyFn(C* arg1, C* arg2) { printf("C,C\n"); }
};

void CallMyFn(A* arg1, A* arg2)
{
  // ideally, with multi-dispatch, at this point the correct MyFn() 
  // would be called, based on the RUNTIME type of arg1 and arg2
  pFoo->MyFn(arg1, arg2);
}

...

A* arg1 = new B();
A* arg2 = new C();
// Using multi-dispatch this would print "B,C"... but because C++ only
// uses single-dispatch it will print out "A,A"
CallMyFn(arg1, arg2);
r3i60tvu

r3i60tvu2#

多分派是指执行的函数依赖于多个对象的运行时类型。
C++有单调度,因为当你使用虚函数时,实际运行的函数只依赖于-〉或.运算符左边的对象的运行时类型。
我很难想出一个真实的的多分派编程案例,也许是在一个不同角色互相战斗的游戏中。

void Fight(Opponent& opponent1, Opponent& opponent2);

战斗的赢家可能取决于双方的特征,因此您可能希望将此调用调度到以下对象之一,具体取决于两个参数的运行时类型:

void Fight(Elephant& elephant, Mouse& mouse)
{
    mouse.Scare(elephant);
}

void Fight(Ninja& ninja, Mouse& mouse)
{
    ninja.KarateChop(mouse);
}

void Fight(Cat& cat, Mouse& mouse)
{
    cat.Catch(mouse);
}

void Fight(Ninja& ninja, Elephant& elephant)
{
    elephant.Trample(ninja);
}

// Etc.

函数的作用取决于两个参数的类型,而不仅仅是一个参数。在C++中,你可能需要把它写成一些虚函数。虚函数的选择取决于一个参数(this指针)。然后,虚函数可能需要包含一个开关或一些东西来对另一个参数做一些特殊的事情。

7ajki6be

7ajki6be3#

single dispatch 中,执行的函数仅取决于对象类型。在 double dispatch 中,执行的函数取决于对象类型和一个参数。
在以下示例中,使用单调度调用函数Area(),而Intersect()依赖于双调度,因为它采用Shape参数。

class Circle;
class Rectangle;
class Shape
{
    virtual double Area() = 0; // Single dispatch

    // ...
    virtual double Intersect(const Shape& s) = 0; // double dispatch, take a Shape argument
    virtual double Intersect(const Circle& s) = 0; 
    virtual double Intersect(const Rectangle& s) = 0; 
};

struct Circle : public Shape
{
    virtual double Area() { return /* pi*r*r */; }

    virtual double Intersect(const Shape& s); 
    { return s.Intersect(*this)  ; }
    virtual double Intersect(const Circle& s); 
    { /*circle-circle*/ }
    virtual double Intersect(const Rectangle& s); 
    { /*circle-rectangle*/ }
};

本示例基于此article

lkaoscv7

lkaoscv74#

展望2022年,在C中有很多技术可以实现多分派
这是一个动态转换(在本文发布时已经可用):https://gist.github.com/jspahrsummers/0834f93cac6d5efa2418ddddf7244b16
这是最近的元程序设计:https://arne-mertz.de/2019/10/multiple-dispatch-over-covariant-functions/
如果你把它和julia或者类似的东西比较一下,它会更复杂,而且可能会有一些性能开销(具体多少有待澄清),这可能会根据C
编译器的不同而有所不同,最终julia的LLVM也会使用某种形式的RTTI。
不确定在最新和最好的C++20中是否可以做得更好。

相关问题