关闭

暴力双分派

1023人阅读 评论(0) 收藏 举报

暴力活是程序员都不想做的,但有的时候你别无选择,但是这种情况下你并不是没有子弹了,暴力可做的事情,多数是类似的重复劳动,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工具。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:179247次
    • 积分:2895
    • 等级:
    • 排名:第12728名
    • 原创:113篇
    • 转载:3篇
    • 译文:0篇
    • 评论:28条
    文章分类