views:

94

answers:

4

Hello,

I need to write a program implementing the visitor design pattern. The problem is that the base visitor class is a template class. This means that BaseVisited::accept() takes a template class as a parameter and since it uses 'this' and i need 'this' to point to the correct runtime instance of the object, it also needs to be virtual.
I'd like to know if there's any way around this problem.

template <typename T>
class BaseVisitor {
  public:
    BaseVisitor();
    T visit(BaseVisited *visited);
    virtual ~BaseVisitor();
}


class BaseVisited {
  BaseVisited();
  template <typename T>
    virtual void accept(BaseVisitor<T> *visitor) { visitor->visit(this); }; // problem
  virtual ~BaseVisited();
}
+4  A: 

What you should do is separate the BaseVisitor.

class BaseVisited;
class BaseVisitorInternal {
public:
    virtual void visit(BaseVisited*) = 0;
    virtual ~BaseVisitorInternal() {}
};
class BaseVisited {
    BaseVisited();
    virtual void accept(BaseVisitorInternal* visitor) { visitor->visit(this); }
};
template<typename T> class BaseVisitor : public BaseVisitorInternal {
    void visit(BaseVisited* visited);
};

If you need BaseVisited's derived classes to be templated too AND pass their correct types/overloads to visit, you're officially dead.

DeadMG
You beat me to it! :-)
Emile Cormier
nyaha :P I forgot the forward declaration of BaseVisited, though.
DeadMG
Basically, this is [type erasure](http://stackoverflow.com/questions/2354210/template-member-function-virtual/2354671#2354671).
sbi
This seems to work. Thanks a lot :)
yurib
+1  A: 

I came up with something slightly different than DeadMG:

class BaseVisited;

class IVisitor {
  public:
    virtual void visit(BaseVisited *visited) = 0;
    virtual ~IVisitor();
};

template <typename T>
class BaseVisitor : public IVisitor {
  public:
    BaseVisitor();
    virtual void visit(BaseVisited *visited);
    virtual ~BaseVisitor();
    virtual T result();
};


class BaseVisited {
  public:
    BaseVisited();
    virtual void accept(IVisitor *visitor) { visitor->visit(this); };
    virtual ~BaseVisited();
};

Mine has an extra result() member function that lets you retrieve the result of the last visit.

Emile Cormier
A: 

Isn't this an exact duplicate of a question from a few weeks ago?

The OP should look at the "curiously recurring template pattern" (CRTP).

Jive Dadson
I did search for questions about the topic on the site before posting, if there was such a question then i missed it or i didn't find the answer i was looking for.
yurib
+1  A: 

You cannot declare/define templated virtual functions. The reason is that the virtual dispatch mechanism must be known when the compiler sees the base class definition, but templates are compiled on demand.

With the common vtable implementation the issue is that the number of entries that the compiler would have to reserve for the virtual function is undefined (how many different instantiations of the type can there be?), as is the order of them. If you declare the class:

class base {
public:
   virtual void foo();
   virtual int bar();
};

The compiler can reserve two entries in the vtable for the pointers to foo and bar in the vtable, and the vtable is perfectly defined by just inspecting the class definition. This cannot be achieved with templated functions.

David Rodríguez - dribeas
I know that its impossible and i understand why, my question was about finding a solution that doesn't involve a template virtual function.thanks anyway though.
yurib
@Yurib: you want a solution and yet you have not stated your problem --I requested that in a comment to the question: What is it that you really want to achieve. You have only asked about a potential solution that does not work, not about what the original problem is.
David Rodríguez - dribeas