нужен обходной путь члена виртуального шаблона - PullRequest
18 голосов
/ 30 мая 2010

Мне нужно написать программу, реализующую шаблон дизайна посетителя. Проблема в том, что базовый класс посетителя является классом шаблона. Это означает, что BaseVisited :: accept () принимает шаблонный класс в качестве параметра, и поскольку он использует 'this', и мне нужно 'this', чтобы указать на правильный экземпляр времени выполнения объекта, он также должен быть виртуальным. Я хотел бы знать, есть ли способ обойти эту проблему.

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

Ответы [ 3 ]

17 голосов
/ 30 мая 2010

То, что вы должны сделать, это отделить 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);
};

Если вам нужно, чтобы производные классы BaseVisited тоже были шаблонными и передавали их правильные типы / перегрузки для посещения, вы официально мертвы.

4 голосов
/ 30 мая 2010

Вы не можете объявлять / определять шаблонные виртуальные функции. Причина в том, что механизм виртуальной диспетчеризации должен быть известен, когда компилятор видит определение базового класса, но шаблоны компилируются по требованию.

В обычной реализации vtable проблема заключается в том, что количество записей, которые компилятор должен зарезервировать для виртуальной функции, не определено (сколько может быть различных экземпляров типа?), А также их порядок. Если вы объявляете класс:

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

Компилятор может зарезервировать две записи в виртуальной таблице для указателей на foo и bar в виртуальной таблице, и виртуальная таблица идеально определяется только путем проверки определения класса. Этого нельзя достичь с помощью шаблонных функций.

4 голосов
/ 30 мая 2010

Я придумал что-то немного отличное от 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 имеет дополнительную result() функцию члена, которая позволяет вам получить результат последнего посещения.

...