没有RTTI的C++双重调度"可扩展"

Geo*_*nn. 6 c++ polymorphism overloading double-dispatch rtti

没有人知道的方式在C正确处理双调度++ 而不使用RTTI和dynamic_cast的<>,并且也是溶液,其中,所述类层次是可扩展的,即基类可以从进一步得到,它的定义/实施不需要知道的吗?
我怀疑没有办法,但我很高兴被证明是错的:)

Jor*_*ans 6

C++中的"访问者模式"通常等同于双重调度.它不使用RTTI或dynamic_casts.

另请参阅此问题的答案.


Jam*_*nze 6

要实现的第一件事是双重(或更高阶)调度不能扩展.使用单一调度和n类型,您需要n功能; 用于双重调度n^2,等等.如何处理此问题部分地决定了如何处理双重调度.一个明显的解决方案是通过创建一个封闭的层次结构来限制派生类型的数量; 在这种情况下,可以使用访问者模式的变体轻松实现双重调度.如果您不关闭层次结构,那么您有几种可能的方法.

如果你坚持认为每一对都对应一个函数,那么你基本上需要一个:

std::map<std::pair<std::type_index, std::type_index>, void (*)(Base const& lhs, Base const& rhs)>
                dispatchMap;
Run Code Online (Sandbox Code Playgroud)

(根据需要调整功能签名.)您还必须实现这些n^2功能,并将它们插入到dispatchMap.(我在这里假设您使用自由函数;没有逻辑上的理由将它们放在其中一个类而不是另一个类中.)之后,您调用:

(*dispatchMap[std::make_pair( std::type_index( typeid( obj1 ) ), std::type_index( typeid( obj2 ) )])( obj1, obj2 );
Run Code Online (Sandbox Code Playgroud)

(你显然希望将它包装到一个函数中;它不是你希望分散在整个代码中的那种东西.)

一个小变体就是说只有某些组合是合法的.在这种情况下,你可以使用 finddispatchMap,并产生一个错误,如果你没有找到你要找的内容.(期待很多错误.)如果您可以定义某种默认行为,则可以使用相同的解决方案.

如果你想100%正确地完成它,一些函数能够处理中间类及其所有衍生物,那么你需要某种更动态的搜索,并命令控制重载决策.考虑例如:

            Base
         /       \
        /         \
       I1          I2
      /  \        /  \
     /    \      /    \
    D1a   D1b   D2a   D2b
Run Code Online (Sandbox Code Playgroud)

如果你有一个f(I1, D2a)和一个f(D1a, I2),应该选择哪一个.最简单的解决方案只是线性搜索,选择可以调用的第一个(由dynamic_cast指向对象的指针确定),并手动管理插入顺序以定义您希望的重载决策.但是,对于n^2函数,这可能会很快变慢.由于存在排序,因此应该可以使用std::map,但是排序功能实际上将是非常重要的(并且仍然必须在dynamic_cast所有地方使用).

考虑到所有事情,我的建议是将双重调度限制为小的,封闭的层次结构,并坚持访问模式的某些变体.

  • @Matthieu我以为他想派出这种类型.在这种情况下,没有RTTI是矛盾的:如果不在运行时确定类型,则无法在运行时根据类型进行调度. (3认同)
  • @James:我们似乎为不同的目的使用相同的单词.对我来说,RTTI特别涉及RunTime类型信息的C++标准实现(用于`typeid`和`dynamic_cast`).请注意,在我提供的LLVM示例中,较弱形式的RTTI支持使用`isa <>`和`cast <>`.您只知道是否可以进行强制转换,而不是拥有完整的运行时信息. (2认同)