转换指向基类的指针列表

发布于 2024-09-26 18:16:58 字数 1233 浏览 2 评论 0原文

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

我很可能错过了一些微妙的东西,并且有一个简单的修复方法。但是,如果这是一个设计缺陷(我在其他帖子中看到过这一点),解决这个问题的适当方法是什么?

假设如下:

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

这些被构造(基于某些输入)到 std::liststd::liststd::liststd::liststd::list<精灵*>。我想做的就是最终获取列表并将其转换为适合输出操作的并行结构。例如,假设:

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>

这导致我提出我的问题。这个问题的最佳解决方案是什么——重新设计还是其他方式?

谢谢。

I have a design where I have a std::list of base pointers that I'd like to transform into a parallel list that adds behavior. The problem I'm having is that the object that I'm trying to use to do the transform doesnt know what the actual types are when it is invoked.

It's quite possible that I'm missing something subtle and that there is an easy fix. However, if this is a design flaw (I've seen this suggested in other posts) what is the appropriate way to approach this?

Suppose the following:

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

These are constructed (based on some input) into a std::list< Sprite * >. What I'd like to do is eventually take the list and transform it into a parallel structure suitable for output operations. For instance, given:

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

I'd ideally like to do something like

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

with something like

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

Now, I get the error

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

Which leads me to my question. What is the best solution to this problem - redesign or otherwise?

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

清音悠歌 2024-10-03 18:16:58

除了 JoshD 的建议之外,您还可以使用访问者模式为其他转换敞开大门。

使用协变返回类型将方法 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;

嗯,重复的函数可以通过模板封装……如果您想要可访问 模板自动判断dispatch_visit的返回类型。如果您不喜欢 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 (); }

In addition to JoshD's suggestion, you can keep the door open for other transformations by using the visitor pattern.

Add a method dispatch_visit to the Sprite hierarchy, using covariant return types:

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 ); }
};

This allows each object to notify the converter of its dynamic type — essentially, a dynamic dispatch into the parallel type, or even a parallel type hierarchy. Everything else pretty much works as your code is written… the best candidate is chosen among the operator()() functions of the converter, based on the static parameter type.

Oh, you'll need to add the "missing function" to the converter:

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, that repetitive function could be encapsulated by a template… this only seems to work in C++0x if you want the visitable template to automatically determine the return type of dispatch_visit. If you don't like principal_base you can factor it out.

#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 (); }
回眸一遍 2024-10-03 18:16:58

我建议为每个类添加转换为 HTML 类型的功能。因此,您将拥有一个虚拟 Convert 函数作为 Sprite 的成员,并且每个派生类都可以重写它。然后,当您在 Sprite * 列表上调用 Convert 函数时,它将调用适当的转换器。您需要转发声明返回类型 (HTMLSprite)。

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

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

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

如果您需要我澄清任何内容,请评论。

I'd suggest adding the function to convert to HTML type to each class. So you'd have a virtual Convert function as a member of Sprite and each derived class can override it. Then when you call the Convert function on your list of Sprite *s, it will call the appropriate converter. You'll need to forward declare the return type (HTMLSprite).

There are probably more elegant ways, but this idea allows you to use virtual functions.

The problem with your suggestion is that the pointers in the array are all type Sprite * regardless of what they actually point to, so a Sprite * is what will be passed to your function. Instead, use something like mem_fun to make a struct that will call the member; this will call the appropriate function through a virtual call:

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

Comment if you need me to clarify anything.

宫墨修音 2024-10-03 18:16:58

抽象工厂怎么样?

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

How about an abstract factory?

       +-----------------+                            +--------+         
       |  RenderFactory  |                            | Sprite |
       |=================|                            +--------+
       | CreateSprite()  |                                 /\
       | CreateMarkup()  |                         +------------------+
       +-----------------+                         |                  |
               /\                            +------------+    +-------------+
       +-----------------+              ....>| HTMLSprite |    | PlainSprite |
       |                 |              :    +------------+    +-------------+
+----------------+   +----------------+ :
| PlainFactory   |   |  HTMLFactory   | :
|================|   |================| :                 +--------+
| CreateSprite() |   | CreateSprite() |.:...              | Markup |
| CreateMarkup() |   | CreateMarkup() |..  :              +--------+
+----------------+   +----------------+    :                  /\
                                           :          +----------------+
                                           :          |                |
                                           :    +------------+   +-------------+
                                            ..> | HTMLMarkup |   | PlainMarkup |
                                                +------------+   +-------------+
送你一个梦 2024-10-03 18:16:58

由于您的转换需要了解要转换的所有类型,并且它也正在执行映射函数。

这是我的快速技巧

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;

As your convert will need to know about all the types to convert from and too it is performing a mapping function.

Here is my quick hack

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;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文