need a virtual template member workaround

2019-01-17 07:48发布

问题:

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

回答1:

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.



回答2:

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.



回答3:

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.