2013-05-27 2 views
1

Связанный вопрос: link.шаблон для шаблонов, полученных по производным классам

В одном из ответов на вопрос выше мне было рекомендовано использовать шаблон посетителя для решения некоторых проблем с моей структурой наследования классов. Тем не менее, я не уверен, можно ли использовать его в моем контексте, так как мои производные классы могут быть не-типами шаблонов.

Чтобы продемонстрировать проблему, я использовал модифицированный пример кода из этого источника: http://sourcemaking.com/design_patterns/visitor/cpp/2. Очевидно, что приведенный ниже пример не компилируется, так как невозможно определить метод виртуального шаблона. Однако, я считаю, код демонстрирует то, чего я пытаюсь достичь. Существуют ли альтернативы/обходные пути?

// 1. Add an accept(Visitor) method to the "element" hierarchy 
class Element 
{ 
    public: 
    virtual void accept(class Visitor &v) = 0; 
}; 

template <unsigned int N> 
class This: public Element 
{ 
    public: 
    /*virtual*/void accept(Visitor &v); 
    string thiss() 
    { 
     return "This"; 
    } 
}; 

class That: public Element 
{ 
    public: 
    /*virtual*/void accept(Visitor &v); 
    string that() 
    { 
     return "That"; 
    } 
}; 

// 2. Create a "visitor" base class w/ a visit() method for every "element" type 
class Visitor 
{ 
    public: 
    template<unsigned int N> 
    virtual void visit(This<N> *e) = 0; 
    virtual void visit(That *e) = 0; 

}; 

template<unsigned int N> 
/*virtual*/void This<N>::accept(Visitor &v) 
{ 
    v.visit(this); 
} 

/*virtual*/void That::accept(Visitor &v) 
{ 
    v.visit(this); 
} 

// 3. Create a "visitor" derived class for each "operation" to do on "elements" 
class UpVisitor: public Visitor 
{ 
    /*virtual*/void visit(This *e) 
    { 
     cout << "do Up on " + e->thiss() << '\n'; 
    } 
    /*virtual*/void visit(That *e) 
    { 
     cout << "do Up on " + e->that() << '\n'; 
    } 

}; 

class DownVisitor: public Visitor 
{ 
    /*virtual*/void visit(This *e) 
    { 
     cout << "do Down on " + e->thiss() << '\n'; 
    } 
    /*virtual*/void visit(That *e) 
    { 
     cout << "do Down on " + e->that() << '\n'; 
    } 

}; 

    int main() 
    { 

     Element *list[] = 
     { 
      new This<3>(), new That() 
     }; 
     UpVisitor up; // 4. Client creates 
     DownVisitor down; // "visitor" objects 
     for (int i = 0; i < 2; i++) list[i]->accept(up); 
     for (int i = 0; i < 2; i++) list[i]->accept(down); 
    } 

ответ

2

Проблема ваша Visitor класс тесно связан с классами, которые являются производными от Element. По мере того, как вы расширяете свой дизайн, это будет больше, чем это уже есть. Вы можете уменьшить/устранить правильную связь, предоставив класс «destination», который определяет все требования к посещаемому объекту. Поскольку имя производных классов является общим атрибутом, вы можете поместить хранилище и получить доступ к нему в класс назначения.

// 1. Define out visitor and destination interfaces 
struct Destination 
{ 
    Destination(const std::string& name) : name_(name) {} 
    virtual std::string ident() const { return name_; } 

    const std::string name_; 
}; 

struct Visitor 
{ 
    virtual void visit(Destination *e) = 0; 
}; 

Это не требует соблюдения требований посетителя от класса Element, который, как представляется, является вашим намерением. Затем ваши классы и That наследуют от Destination и предоставляют необходимые реализации.

// 2. Define our element and it's derived classes 
class Element 
{ 
public: 
    virtual void accept(class Visitor &v) = 0; 
}; 

template <unsigned int N> 
class This: public Element, public Destination 
{ 
public: 
    This() : Destination("This") {} 
    virtual void accept(Visitor &v) 
    { 
     v.visit(this); 
    } 
}; 

class That: public Element, public Destination 
{ 
public: 
    That() : Destination("That") {} 
    virtual void accept(Visitor &v) 
    { 
     v.visit(this); 
    } 
}; 

Теперь ваш вверх и вниз посетителей упрощены в нечто вроде следующего

// 3. Create a "visitor" derived class for each "operation" to do on "elements" 
class UpVisitor: public Visitor 
{ 
    void visit(Destination *e) { 
     cout << "do Up on " + e->ident() << '\n'; 
    } 
}; 

class DownVisitor: public Visitor 
{ 
    void visit(Destination *e) { 
     cout << "do Down on " + e->ident() << '\n'; 
    } 
}; 

Хотя я не изменил его в растворе выше, я рекомендую изменения visit взять ссылку вместо указателя. Поскольку C++ не имеет понятия нулевой ссылки, это указывает на то, что Destination соответствует , где в качестве указателя можно было бы назвать необязательным.

Смежные вопросы