C++ 中的双重调度

Double Dispatch in C++

我需要以下类型的调度功能。在我的应用程序中,我真正拥有的是指向状态基 class (foo) 的指针以及指向测量基 class (bar) 的指针。基于传递给 dispatch2 函数的派生实例,我需要在给定状态的情况下生成估计测量值。例如,派生状态 class 可以是位置,派生测量 class 可以是 ToF(飞行时间)。然后,处理程序将从 f(例如发射器位置)获取状态信息以及从 b(例如传感器位置)获取收集器信息,并根据这些信息计算预期 ToF。然后将其返回并可以与实际测量值 (b) 进行比较。

string dispatch2(foo* f, bar* b) {
  if      ( dynamic_cast<Foo>(f) )  return foo1(f,b);
  else if ( dynamic_cast<FOo>(f) )  return foo2(f,b);
  else if ( dynamic_cast<FOO>(f) )  return foo3(f,b);
  throw std::runtime_error("dispatch for f not defined");
}

string foo1(foo* f, bar* b) {
  if      ( dynamic_cast<Bar>(b) )  return foo1bar1handler(f,b);
  else if ( dynamic_cast<BAR>(b) )  return foo1bar2handler(f,b);
  throw std::runtime_error("foo1: dispatch for b not defined");
}

string foo2(foo* f, bar* b) {
  if      ( dynamic_cast<Bar>(b) )  return foo2bar1handler(f,b);
  else if ( dynamic_cast<BAR>(b) )  return foo2bar2handler(f,b);
  throw std::runtime_error("foo2: dispatch for b not defined");
}

string foo3(foo* f, bar* b) {
  if      ( dynamic_cast<Bar>(b) )  return foo3bar1handler(f,b);
  else if ( dynamic_cast<BAR>(b) )  return foo3bar2handler(f,b);
  throw std::runtime_error("foo3: dispatch for b not defined");
}

string foo1bar1handler(foo* f, bar* b) {return "FooBar";}
string foo2bar2handler(foo* f, bar* b) {return "FooBAR";}
string foo3bar1handler(foo* f, bar* b) {return "FOoBar";}
string foo2bar2handler(foo* f, bar* b) {return "FOoBAR";}
string foo2bar1handler(foo* f, bar* b) {return "FOOBar";}
string foo2bar2handler(foo* f, bar* b) {return "FOOBAR";}

显然,没有办法避免为我想明确处理的每个组合定义结束方法的需要。但是,我正在寻找其他方法来实现这一点。理想情况下,某些模式允许用户显式注册每个处理程序,并且任何未处理的组合都可能引发运行时异常。任何建议,将不胜感激。谢谢

一种方法(当然不是唯一的方法)是调用 foo 上的虚函数,将其传递给 bar。 foo 的每个派生类型都以相同的方式实现此调度函数,它将自身传递给 bar 中的虚拟处理程序函数。 当您需要添加更多内容时,您可以扩展接口以接受新类型。所有 foo 函数都具有相同的实现,但它们有所不同,因此 "this" 是对象的正确动态类型。

另外,Andrei Alexandrescu 在他的(现在不那么现代的)书 Modern C++ Design 中用设计替代方案对此进行了很好的调查,该书仍然涵盖了这个想法,但是为 c++98 编写,但绝对仍然值得一读(尽管它说不能做的许多事情现在是 C++ 的一部分,部分原因是那本书。)

现场观看:https://godbolt.org/z/oRyVJx

此示例有 3 个 Foo 类 和 2 个 Bar 类。

#include <iostream>

class BarBase;

class FooBase { 
public:
    virtual ~FooBase() = default;
    virtual void dispatch(BarBase*) = 0;
};

class Foo1;
class Foo2;
class Foo3;

class BarBase {
public:
    virtual ~BarBase() = default;
    virtual void accept(Foo1*) = 0;
    virtual void accept(Foo2*) = 0;
    virtual void accept(Foo3*) = 0;
};

class Bar1 : public BarBase {
public:
    void accept(Foo1*) override;
    void accept(Foo2*) override;
    void accept(Foo3*) override;
};

class Bar2 : public BarBase {
public:
    void accept(Foo1*) override;
    void accept(Foo2*) override;
    void accept(Foo3*) override;
};

class Foo1 : public FooBase {
public:
    void dispatch(BarBase* bar) override { bar->accept(this); }
};

class Foo2 : public FooBase {
public:
    void dispatch(BarBase* bar) override { bar->accept(this); }
};

class Foo3 : public FooBase {
public:
    void dispatch(BarBase* bar) override { bar->accept(this); }
};

void Bar1::accept(Foo1 * f) { std::cout << "Bar1 accepting Foo1\n"; }
void Bar1::accept(Foo2 * f) { std::cout << "Bar1 accepting Foo2\n"; }
void Bar1::accept(Foo3 * f) { std::cout << "Bar1 accepting Foo3\n"; }
void Bar2::accept(Foo1 * f) { std::cout << "Bar2 accepting Foo1\n"; }
void Bar2::accept(Foo2 * f) { std::cout << "Bar2 accepting Foo2\n"; }
void Bar2::accept(Foo3 * f) { std::cout << "Bar2 accepting Foo3\n"; }

//
// Doesn't know which types of foo and bar it has, but it doesn't matter...
//
void call(FooBase& foo, BarBase& bar) {
    foo.dispatch(&bar);
}

int main() {
    Foo1 f1;
    Foo2 f2;
    Foo3 f3;
    Bar1 b1;
    Bar2 b2;

    call(f1, b1);
    call(f2, b1);
    call(f3, b1);
    call(f1, b2);
    call(f2, b2);
    call(f3, b2);
}

输出:

Bar1 accepting Foo1
Bar1 accepting Foo2
Bar1 accepting Foo3
Bar2 accepting Foo1
Bar2 accepting Foo2
Bar2 accepting Foo3