23

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();
}
yurib
  • 8,043
  • 3
  • 30
  • 55
  • 2
    it wont compile. http://stackoverflow.com/questions/2354210/template-member-function-virtual – yurib May 30 '10 at 18:54
  • 2
    Compiler won't accept templates in virtual functions. – Puppy May 30 '10 at 18:54
  • 1
    Why do you want to make the visitor a template? What is it that you want to solve with this construct? What you want to do cannot be done, but there might be other ways of implementing what you really need. – David Rodríguez - dribeas May 30 '10 at 19:22

3 Answers3

17

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.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • 4
    Basically, this is [type erasure](http://stackoverflow.com/questions/2354210/template-member-function-virtual/2354671#2354671). – sbi May 30 '10 at 19:54
4

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
  • 28,391
  • 15
  • 94
  • 122
2

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
  • 204,818
  • 23
  • 294
  • 489
  • 3
    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 May 30 '10 at 19:50
  • @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 May 31 '10 at 07:06