代码之家  ›  专栏  ›  技术社区  ›  ezpz

将指针列表转换为基类

  •  2
  • ezpz  · 技术社区  · 15 年前

    我有一个设计 std::list 我要转换为添加行为的并行列表的基指针。我遇到的问题是,我试图用来进行转换的对象在被调用时不知道实际的类型是什么。

    很有可能我遗漏了一些微妙的东西,而且有一个简单的解决方法。但是,如果这是一个设计缺陷(我在其他文章中看到过这一点),那么什么是解决这个问题的合适方法?

    假设如下:

    class Sprite { /* ... */ };
    class Character : public Sprite {};
    class Markup : public Sprite {};
    

    它们(基于一些输入)被构造成 std::list< Sprite * > . 我想做的是最终获取列表并将其转换为适合输出操作的并行结构。例如,给定:

    class HTMLSprite { /* ... */ };
    class HTMLCharacter : public HTMLSprite {};
    class HTMLMarkup : public HTMLSprite {};
    

    我最理想的做法是

    std::transform(sprites.begin (), sprites.end (), html.begin (), HTMLConvert);
    

    有点像

    struct HTMLConvert_ {
      HTMLSprite * operator () (const Character * c) { return new HTMLCharacter (); }
      HTMLSprite * operator () (const Markup * c) { return new HTMLMarkup (); }
    } HTMLConvert;
    

    现在,我明白了

    call of `(HTMLConvert) (const Sprite* const&)' is ambiguous
    HTMLSprite* HTMLConvert::operator()(const Character*) const <near match>
    

    这就引出了我的问题。这个问题的最佳解决方案是什么-重新设计还是其他?

    谢谢。

    4 回复  |  直到 15 年前
        1
  •  3
  •   Potatoswatter    15 年前

    除了JoshD的建议之外,您还可以使用 visitor pattern .

    添加方法 dispatch_visit 到精灵层次结构,使用协变返回类型:

    class Sprite{
        virtual HTMLSprite * dispatch_visit( HTMLConvert_ const &c ) const = 0;
    };
    class Character : public Sprite {
        virtual HTMLCharacter * dispatch_visit( HTMLConvert_ const &c ) const
            { return c( this ); }
    };
    class Markup : public Sprite {
        virtual HTMLMarkup * dispatch_visit( HTMLConvert_ const &c ) const
            { return c( this ); }
    };
    

    这允许每个对象通知转换器其动态类型,动态分派到并行类型,甚至并行类型层次结构。当你的代码被编写时,其他的一切都能正常工作,最好的候选者是在 operator()() 转换器的功能,基于静态参数类型。

    哦,你需要在转换器中添加“缺少的功能”:

    struct HTMLConvert_ {
      HTMLSprite * operator () (const Sprite * c) { return c->dispatch_visit( *this ); }
      HTMLCharacter * operator () (const Character * c) { return new HTMLCharacter (); }
      HTMLMarkup * operator () (const Markup * c) { return new HTMLMarkup (); }
    } HTMLConvert;
    

    HMM,重复功能可以被模板封装起来,如果你想要的话,它只在C++ 0x中工作。 visitable 自动确定返回类型的模板 派遣访问 . 如果你不喜欢 principal_base 你可以考虑一下。

    #include <functional>
    
    template< class T >
    struct principal_base
        { typedef void type; };
    
    template< class Client, class Visitor,
        class Base = typename principal_base< Client >::type >
    struct visitable :
        virtual visitable< typename principal_base< Client >::type, Visitor > {
        virtual typename std::result_of< Visitor( Client * ) >::type
        dispatch_visit( Visitor const &v ) const
            { return v( static_cast< Client const * >( this ) ); }
    };
    
    template< class Client, class Visitor >
    struct visitable< Client, Visitor, void > {
        virtual typename std::result_of< Visitor( Client * ) >::type
        dispatch_visit( Visitor const &v ) const = 0;
    };
    
    class HTMLSprite { /* ... */ };
    class HTMLCharacter : public HTMLSprite {};
    class HTMLMarkup : public HTMLSprite {};
    
    class Sprite;
    class Character;
    class Markup;
    
    struct HTMLConvert_ {
      HTMLSprite * operator () (const Sprite * c);
      HTMLCharacter * operator () (const Character * c);
      HTMLMarkup * operator () (const Markup * c);
    } HTMLConvert;
    
    class Sprite : public visitable< Sprite, HTMLConvert_ > {};
    template<> struct principal_base< Character >
        { typedef Sprite type; };
    class Character : public Sprite, visitable< Character, HTMLConvert_ > {};
    template<> struct principal_base< Markup >
        { typedef Sprite type; };
    class Markup : public Sprite, visitable< Markup, HTMLConvert_ > {};
    
    //class Invalid : Character, Markup {};
    
    HTMLSprite * HTMLConvert_::operator () (const Sprite * c)
        { return c->dispatch_visit( *this ); }
    HTMLCharacter * HTMLConvert_::operator () (const Character * c)
        { return new HTMLCharacter (); }
    HTMLMarkup * HTMLConvert_::operator () (const Markup * c)
        { return new HTMLMarkup (); }
    
        2
  •  2
  •   JoshD    15 年前

    我建议在每个类中添加转换为HTML类型的函数。所以你会有一个虚拟的 Convert 作为 Sprite 每个派生类都可以重写它。当你打电话给 转换 功能在您的列表中 Sprite * s、 它将调用适当的转换器。您需要转发声明返回类型( HTMLSprite ).

    可能有更优雅的方法,但是这个想法允许您使用虚拟函数。

    您的建议的问题是数组中的指针都是类型 雪碧* 不管他们实际指的是什么,所以 雪碧* 将传递给您的函数。相反,使用类似mem_fun的东西来创建一个调用成员的结构;这将通过虚拟调用调用适当的函数:

    std::transform(sprites.begin (), sprites.end (), html.begin (), mem_fun(&Sprite::Convert));
    

    如果你需要我澄清什么,请评论。

        3
  •  1
  •   wilhelmtell    15 年前

    抽象工厂怎么样?

           +-----------------+                            +--------+         
           |  RenderFactory  |                            | Sprite |
           |=================|                            +--------+
           | CreateSprite()  |                                 /\
           | CreateMarkup()  |                         +------------------+
           +-----------------+                         |                  |
                   /\                            +------------+    +-------------+
           +-----------------+              ....>| HTMLSprite |    | PlainSprite |
           |                 |              :    +------------+    +-------------+
    +----------------+   +----------------+ :
    | PlainFactory   |   |  HTMLFactory   | :
    |================|   |================| :                 +--------+
    | CreateSprite() |   | CreateSprite() |.:...              | Markup |
    | CreateMarkup() |   | CreateMarkup() |..  :              +--------+
    +----------------+   +----------------+    :                  /\
                                               :          +----------------+
                                               :          |                |
                                               :    +------------+   +-------------+
                                                ..> | HTMLMarkup |   | PlainMarkup |
                                                    +------------+   +-------------+
    
        4
  •  0
  •   Greg Domjan    15 年前

    因为转换程序需要知道要转换的所有类型,而且它还执行映射函数。

    这是我的快速技巧

    struct HTMLConvert_ {
      HTMLSprite * operator () (const Sprite* const& sp ) { 
        Character const * c = dynamic_cast<Character const *>(sp);
        if( c )
          return new HTMLCharacter (c);
    
        Markup const * m = dynamic_cast<Markup const *>(sp);
        if( c )
          return new HTMLMarkup (m);
      }
    } HTMLConvert;