5

I understand why member template functions cannot be virtual, but I'm not sure what the best workaround is.

I have some code similar to this:

struct Entity
{
    template<typename It>
    virtual It GetChildren(It it) { return it; }
};

struct Person : public Entity
{
    template<typename It>
    virtual It GetChildren(It it) { *it++ = "Joe"; }
};

struct Node : public Entity
{
    Node left, right;
    const char *GetName() { return "dummy"; }

    template<typename It>
    virtual It GetChildren(It it)
    {
        *it++ = left.GetName();
        *it++ = right.GetName();
        return it;
    }
};

Clearly, I need dynamic dispatch. But given that the classes are actually pretty large, I don't want to template the entire class. And I still want to support any kind of iterator.

What's the best way to achieve this?

Community
  • 1
  • 1
user541686
  • 205,094
  • 128
  • 528
  • 886

3 Answers3

3

If supporting any kind of iterator is the only thing you want, you may use an iterator that uses type erasure. I think there is an any_iterator implemented somewhere in the Boost Sandbox/Vault or Adobe Labs or one of them. Here is the first result from Google:

http://thbecker.net/free_software_utilities/type_erasure_for_cpp_iterators/any_iterator.html

K-ballo
  • 80,396
  • 20
  • 159
  • 169
  • Hm... so that would pretty much create a double-dynamic-dispatch mechanism, one for the class and one for the iterator? – user541686 Nov 11 '11 at 21:04
2

Since you do not want to template whole class, and don't want to use an iterator with type erasure, the only solution is to write your own iterator.

By the way, your example looks weird. Maybe you can take a look on how to improve the design, which would make your problem go away.

BЈовић
  • 62,405
  • 41
  • 173
  • 273
1

You don't want to generically "support any kind of iterator."

You want to satisfy the STL Container requirements to provide an iterator that users can generically support. Make an iterator, then provide an iterator typedef for your container:

class Entity_Iterator {
public:
    Entity_Iterator operator++() { /* et cetera */ }
    Entity& operator*() const { /* et cetera */ }
private:
    Entity* entity_ptr_;
    // et cetera
}

class Entity_Container {
public:
    typedef Entity_Iterator iterator;
    iterator begin() const { /* et cetera */ }
    iterator end() const  { /* et cetera */ }
private:
    Entity* head_;
    // et cetera
}

Now a user can follow the STL Container convention to use your container, and you'll be able to apply STL Algorithms to your container as well:

Entity_Container x;
Entity_Container::iterator i = x.begin();
i->GetName();

Edit add link:

http://www.sgi.com/tech/stl/Container.html

Edit

I'm trying to Google a link to a complete C++ code stub template for implementing an STL Container that I remember seeing once, but I'm finding it very difficult to Google STL template.

Edit

Do it this way, and you'll be able to write your container as a tree structure that can contain heterogeneous types inherited from Entity with virtual methods, which it looks like is what you're trying to do?

Or perhaps not?

James Brock
  • 3,236
  • 1
  • 28
  • 33
  • I'm confused, how does this work with polymorphism? i.e. what would be the return value of `Entity::begin()`, for example? It can't be a concrete `iterator` because the type would depend on the derived type of `Entity` (since different classes have different means of iterating through their children). So would I need to return some kind of pointer to an iterator? – user541686 Nov 12 '11 at 00:50
  • Consider the `Entity_Iterator` stub that I've created: it has a pointer to an `Entity`. If there is a virtual `Entity::GetName()` function, then you can call it through any `Entity`-derived type that the iterator refers to, see `i->GetName()`, above. – James Brock Nov 12 '11 at 01:12
  • Oh, the problem is, there *isn't* a virtual `GetName()` function -- that's precisely why I didn't put one inside the `Person` class in the example. (The fact that you can modify the example to fit this design is beside the point... I was just illustrating the problem with something trivial and easy to understand, which shouldn't imply that the actual design is as easy to "fix" as the example..) – user541686 Nov 12 '11 at 01:28
  • I'm upvoting VJo and K-ballo's answers, which are better answers than mine. – James Brock Nov 12 '11 at 01:38