C++ 中的多重调度

问题描述 投票:0回答:5

我想了解什么是多重调度。我读了很多不同的文本,但我仍然不知道多重调度是什么以及它有什么好处。也许我缺少的是使用多重调度的代码片段。请问,您能否使用多重分派在 C++ 中编写一小段代码,以便我可以看到它无法正确编译/运行,因为 C++ 只有单分派?我需要看看其中的区别。谢谢。

c++ multiple-dispatch
5个回答
93
投票

多重调度是能够根据传递给函数调用的参数的运行时类型来选择要调用的函数版本。

这是一个在 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);

25
投票

多重分派是指执行的函数取决于多个对象的运行时类型。

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 指针)来选择虚拟函数。然后,虚函数可能需要包含一个开关或其他东西来对另一个参数执行特定的操作。


3
投票

单次调度中,执行的函数仅取决于对象类型。在 双重调度执行的函数取决于对象类型和a 范围。

在以下示例中,函数

Area()
被调用 单次调度,而
Intersect()
依赖于双次调度,因为它需要 形状参数。

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

该示例基于这篇文章


3
投票

从 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 中你是否可以做得更好。


0
投票

如果您想使用基于重载集的value语义传播进行多次调度,您可以使用这个库(我的):https://gitlab.com/correaa/boost-covariant

© www.soinside.com 2019 - 2024. All rights reserved.