将指针列表转换为基类

时间:2010-10-08 00:31:47

标签: c++ polymorphism transform

我有一个设计,我有一个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 个答案:

答案 0 :(得分:3)

除了JoshD的建议外,您还可以使用visitor pattern为其他转换打开大门。

使用协变返回类型将方法dispatch_visit添加到Sprite层次结构:

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;

嗯,这个重复的函数可以用模板封装......如果你希望visitable模板自动确定dispatch_visit的返回类型,这似乎只适用于C ++ 0x。如果您不喜欢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 (); }

答案 1 :(得分:2)

我建议添加函数以将HTML类型转换为每个类。所以你有一个虚拟的Convert函数作为Sprite的成员,每个派生类都可以覆盖它。然后,当您在Convert列表中调用Sprite *函数时,它将调用相应的转换器。您需要转发声明返回类型(HTMLSprite)。

可能有更优雅的方式,但这个想法允许你使用虚拟功能。

你的建议的问题是数组中的指针都是Sprite *类型,无论它们实际指向什么,因此Sprite *将传递给你的函数。相反,使用类似mem_fun的东西来创建一个将调用该成员的结构;这将通过虚拟调用调用适当的函数:

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

评论你是否需要我澄清任何事情。

答案 2 :(得分:1)

抽象工厂怎么样?

       +-----------------+                            +--------+         
       |  RenderFactory  |                            | Sprite |
       |=================|                            +--------+
       | CreateSprite()  |                                 /\
       | CreateMarkup()  |                         +------------------+
       +-----------------+                         |                  |
               /\                            +------------+    +-------------+
       +-----------------+              ....>| HTMLSprite |    | PlainSprite |
       |                 |              :    +------------+    +-------------+
+----------------+   +----------------+ :
| PlainFactory   |   |  HTMLFactory   | :
|================|   |================| :                 +--------+
| CreateSprite() |   | CreateSprite() |.:...              | Markup |
| CreateMarkup() |   | CreateMarkup() |..  :              +--------+
+----------------+   +----------------+    :                  /\
                                           :          +----------------+
                                           :          |                |
                                           :    +------------+   +-------------+
                                            ..> | HTMLMarkup |   | PlainMarkup |
                                                +------------+   +-------------+

答案 3 :(得分:0)

由于您的转换需要知道要转换的所有类型,因此它正在执行映射功能。

这是我的快速黑客

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;