暴力双分派

原创 2007年09月20日 11:39:00

暴力活是程序员都不想做的,但有的时候你别无选择,但是这种情况下你并不是没有子弹了,暴力可做的事情,多数是类似的重复劳动,C++编译器可以帮我们做这些,只不过你要给编译器一个生成规则,模板是一个描述这种规则的工具。

我来展示一个极其野蛮的行为,有如下4个class:

class MyA;
class MyB;
class MyC;
class MyD;

为了对其双分配,我们需要如下方法:

void    doSomething(MyA&,MyA&);
void    doSomething(MyA&,MyB&);
void    doSomething(MyA&,MyC&);
void    doSomething(MyA&,MyD&);
void    doSomething(MyB&,MyB&);
void    doSomething(MyB&,MyC&);
void    doSomething(MyB&,MyD&);
void    doSomething(MyC&,MyC&);
void    doSomething(MyC&,MyD&);
void    doSomething(MyD&,MyD&);

其实2个或3个类就足以说明这个问题,所以我说这是一件极其野蛮的事情,因为使用了4个类。对于双分派的选择,情况是这样的:

void    DoubleDispach(MyBase& lhs,MyBase& rhs)
...{
    
if (MyA* p = dynamic_cast<MyA*>(&lhs))
    
...{
        
if (MyA* q = dynamic_cast<MyA*>(&rhs))
        
...{
            doSomething(
*p,*q);
        }

        
else if (MyB* q = dynamic_cast<MyB*>(&rhs))
        
...{
            doSomething(
*p,*q);
        }

        
else if (MyC* q = dynamic_cast<MyC*>(&rhs))
        
...{
            doSomething(
*p,*q);
        }

        
else if(MyD* q = dynamic_cast<MyD*>(&rhs))
        
...{
            doSomething(
*p,*q);
        }

        
else
        
...{
            doSomething(lhs,rhs);
        }

    }

    
else if(MyB* p = dynamic_cast<MyB*>(&lhs))
    
...{
        
if (MyA* q = dynamic_cast<MyA*>(&rhs))
        
...{
            doSomething(
*q,*p);
        }

        
else if (MyB* q = dynamic_cast<MyB*>(&rhs))
        
...{
            doSomething(
*p,*q);
        }

        
else if (MyC* q = dynamic_cast<MyC*>(&rhs))
        
...{
            doSomething(
*p,*q);
        }

        
else if(MyD* q = dynamic_cast<MyD*>(&rhs))
        
...{
            doSomething(
*p,*q);
        }

        
else
        
...{
            doSomething(lhs,rhs);
        }

    }

    
else if(MyC* p = dynamic_cast<MyC*>(&lhs))
    
...{
        
if (MyA* q = dynamic_cast<MyA*>(&rhs))
        
...{
            doSomething(
*q,*p);
        }

        
else if (MyB* q = dynamic_cast<MyB*>(&rhs))
        
...{
            doSomething(
*q,*p);
        }

        
else if (MyC* q = dynamic_cast<MyC*>(&rhs))
        
...{
            doSomething(
*p,*q);
        }

        
else if(MyD* q = dynamic_cast<MyD*>(&rhs))
        
...{
            doSomething(
*p,*q);
        }

        
else
        
...{
            doSomething(lhs,rhs);
        }

    }

    
else
    
...{
        
if (MyA* q = dynamic_cast<MyA*>(&rhs))
        
...{
            doSomething(
*q,*p);
        }

        
else if (MyB* q = dynamic_cast<MyB*>(&rhs))
        
...{
            doSomething(
*q,*p);
        }

        
else if (MyC* q = dynamic_cast<MyC*>(&rhs))
        
...{
            doSomething(
*q,*p);
        }

        
else if(MyD* q = dynamic_cast<MyD*>(&rhs))
        
...{
            doSomething(
*p,*q);
        }

        
else
        
...{
            doSomething(lhs,rhs);
        }

    }

}

 这真是太野蛮了,是的,我要让你看到的就是这样,实际上,碰到这样的问题,不管最后采用什么手段,最终的代码逻辑都是这样的,现在我们来考虑如何驱动编译器帮我们做这些无聊的事情。

我们必须有如下类型:

template
<
    
class Exec,
    
class LType,
    
class LTList,
    
class RType,
    
class RTList,
    typename    ResultType
>

这是双分派时的参与者们。

使用模板就是驱动编译器生成代码,所以驱动规则的时候请参考你写代码的逻辑,你考虑问题的逻辑被合理分析总结就可以被编译器合理使用,其实,这个过程中想到你是如何想到这个问题的解决方法的过程是非常重要的,语言如此的拗口,实际上,这个思想的行为和语言一样拗口。

按照我们写代码的逻辑,我们需要若干的最终执行者,它们被封装在Exec类里。我们需要lhs类型检索,然后rhs类型检索,如此我们有如下几个方法。

第一次分配:

template
<
    
class Exec,
    
class LType,
    
class LTList,
    
class RType,
    
class RTList,
    typename    ResultType
>
class StaticDispatcher
...{
    typedef    typename    LTList::Head    Head;
    typedef    typename    LTList::Tail    Tail;
    
static    ResultType    FirstDispach(LType& lhs,RType& rhs,Exec exc)
    
...{
        
if (Head* p1 = dynamic_cast<Head*>(&lhs))
        
...{
            
return    
                StaticDispatcher
<Exec,LType,NullType,RType,RTList>::SecondDispach(*p1,rhs,exc);
        }

        
else
        
...{
            
return    
                StaticDispatcher
<Exec,LType,Tail,RType,RTList>::FirstDispach(lhs,rhs,exc);
        }

    }

    
static    ResultType    FirstDispach(NullType& lhs,RType& rhs,Exec exc)
    
...{
        
return    exc.OnError(lhs,rhs);
    }

}
;

 

如果找不到特定类型(TList走到了NullType),依据偏特化我们进行错误处理。

第二次分派:

template
<
    
class Exec,
    
class LType,
    
class LTList,
    
class RType,
    
class RTList,
        typename    ResultType
>
class StaticDispatcher
...{
    typedef    typename    RTList::Head    Head;
    typedef    typename    RTList::Tail    Tail;
    template 
<typename SomeType>
    
static    ResultType    SecondDispach(SomeType& lhs,RType& rhs,Exec exc)
    
...{
        
if (Head* p1 = dynamic_cast<Head*>(&rhs))
        
...{
            
return    exc.doSomething(lhs,*p1);
        }

        
else
        
...{
            
return    
                StaticDispatcher
<Exec,SomeType,NullType,RType,Tail>::SecondDispach(lhs,rhs,exc);
        }

    }

    template    
<typename SomeType>
    
static    ResultType    SecondDispach(SomeType& lhs,NullType& rhs,Exec exc)
    
...{
        
return    exc.OnError(lhs,rhs);
    }

}
;

和第一次分派的想法一样。

我用非常朴素的想法描述了双分派的代码,Loki里有更好的东西。

这里有一个问题,就是子类可以合理向基类转换,这就要求我们的TList必须是有特定顺序的,幸好,我们有DerivedToFront工具。

实现C++双分派

/******************************************************************** author : Clark/陈泽丹 create...
  • xiaodan007
  • xiaodan007
  • 2014年04月23日 05:07
  • 902

双分派模式

在《GoF 23种设计模式模式解析附C++实现源码》和《设计模式解析之—Visitor模式》中,我给出了Visitor模式的诠释和示例实现源码。  个人觉得例子和解析还是能够比较清晰地为学习和掌握V...
  • mydriverc2
  • mydriverc2
  • 2013年09月26日 10:55
  • 510

5.1Java模拟 双分派(Double Dispatch)

应用命令模式,在Java中模拟双分派。
  • yqj2065
  • yqj2065
  • 2014年08月31日 14:41
  • 2573

Double Dispatch(双分派)(c++ 版)

参考文献地址: http://gurudk.iteye.com/blog/322753 分派过程就是确定一个方法调用的过程,双分派就是根据运行时多个对象的类型确定方法调用的过程。 想象这样一个客户...
  • wzsy
  • wzsy
  • 2016年08月19日 10:41
  • 189

面向对象语言的多分派、单分派、双重分派

变量被声明时的引用类型是变量的静态类型,真实类型是变量的实际类型。根据变量的类型进行方法的选择就是分派。在编译阶段产生静态分派,根据静态类型发生,比如方法重载,发生在运行阶段的是动态分派,动态置换调用...
  • ckao7470
  • ckao7470
  • 2014年02月10日 17:05
  • 1737

java方法调用之单分派与多分派(二)

上篇博文java方法调用之重载、重写的调用原理(一) 讨论了重写与重载的实现原理,这篇博文讨论下单分派与多分派。单分派、多分派方法的接收者和方法的参数统称为方法的宗量。 根据分派基于宗量多少(接收者是...
  • fan2012huan
  • fan2012huan
  • 2016年03月29日 17:00
  • 2177

暴力双分派再论TList

TypeList是Loki从外星引进的强大引擎, TypeList由模板支持构建,但是被构建出来的TypeList给了模板神奇的能力,Loki给予新知,源于模板,Loki给予震撼,源于TypeList...
  • win2ks
  • win2ks
  • 2007年09月20日 13:30
  • 601

访问者模式 双重分派分析 与 accept方法存在的必要性

以前看过大话设计模式,对各种模式有过一点了解,可是没怎么用,可没怎么深入去了解所以忘得很快,现在沉下心来好好研究下。但是,在看到访问者模式的时候,我对accept存在的必要性很是不理解,觉得为何不直接...
  • luohaowang320
  • luohaowang320
  • 2014年02月24日 18:19
  • 1110

双分派的代码

暴力活是程序员都不想做的,但有的时候你别无选择,但是这种情况下你并不是没有子弹了,暴力可做的事情,多数是类似的重复劳动,C++编译器可以帮我们做这些,只不过你要给编译器一个生成规则,模板是一个描述这种...
  • xiaodan007
  • xiaodan007
  • 2014年04月23日 02:15
  • 507

访问者模式与双分派

Java中模拟双分派  作为命令模式的应用,得到了一个有用的结构。从该结构出发唾手可得访问者模式。 再次说明,yqj2065不关心访问者模式的“对象结构”。 双分派中的结构,如图所示。 如果一个...
  • yqj2065
  • yqj2065
  • 2015年09月25日 16:35
  • 761
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:暴力双分派
举报原因:
原因补充:

(最多只允许输入30个字)