Возврат значения из шаблона посетителя

У меня есть следующая иерархия:

         class Element{ public : virtual void Accept(Visitor&) = 0
                 protected : Element();   int num;
          };

         class ElementA : public Element{
                 public : ElementA(); 
                 void Accept(Visitor& v) {v.Visit(this};}
         };

         class ElementB : public Element{
                 public : ElementB(); 
                 void Accept(Visitor& v) {v.Visit(this};}


         class Visitor{
                 public: void Visit(ElementA*);
                 void Visit(ElementB*);
         };

EDIT: необходимо добавить метод int getNum() в иерархию, которая будет предоставлять значение num. Однако для этого потребуется заново скомпилировать всю иерархию, а нам это не разрешено. Таким образом, мы должны изменить дизайн иерархии таким образом, чтобы перекомпиляция иерархии не потребовалась.


person Community    schedule 05.04.2013    source источник
comment
Как вы собираетесь возвращать значение из нескольких элементов? Или вы можете смириться с тем, что не знаете, какое значение получено от какого элемента?   -  person Dariusz    schedule 05.04.2013


Ответы (4)


То, что вы хотите сделать, невозможно сделать чисто спроектированным способом. Я понятия не имею, почему полная перекомпиляция этой иерархии может быть такой проблемой, но есть решение, которое технически возможно без использования хаков UB, таких как reinterpret_cast отключение защиты доступа и другие хаки.

int Visitor::getNum(Element* pe)
{ 
  //define a pick-pocket... ;)
  struct ElementNumAccessor : private Element
  {
    ElementNumAccessor(Element const& e) : Element(e) {}   
    int getNum() { return num; }

    void Accept(Visitor&); //has to be declared, but needs not be defined
  };

  //...and let him work:
  ElementNumAccessor ea(*pe);
  return ea.getNum();
}

Эта идея в действии: http://ideone.com/e1chSX
Здесь используется тот факт, что защищенный доступ транзитивно, но происходит за счет копии каждого элемента, из которого вы хотите получить число. Я сделал структуру локальным классом функции, чтобы никому не пришло в голову использовать ее для каких-либо других целей.

Но имейте в виду, что этот метод является хаком, использованием языковой функции, которая не предназначена для использования таким образом.

Мой совет: если ваша иерархия настолько запутана в программе, что ее изменение приводит к ужасу перекомпиляции, то пришло время реорганизовать вашу программу, чтобы уменьшить зависимости времени компиляции, а затем внести необходимые изменения.

person Arne Mertz    schedule 05.04.2013
comment
немного изменил его - вам не нужно копировать элементы - person Arne Mertz; 05.04.2013
comment
Хм, это разрешено? Вы обращаетесь к защищенному члену аргумента, который не относится к тому же классу. Я знаю, что ваше предложение сработает, если вы взяли ElemNumAccessor в качестве входных данных, например getNum(ElemNumAccessor) (поскольку ограничения доступа относятся к классу, а не к члену). Но доступ к членам аргумента базового класса не должен работать: вопросы/7476117/ - person maditya; 05.04.2013
comment
хм, я посмотрю - следите за обновлениями - person Arne Mertz; 05.04.2013
comment
Конечно. :) Вопрос все еще кажется подозрительным, но это хороший способ обойти его. - person maditya; 05.04.2013

Одна из возможностей — объявить GetNumVisitor другом Element class и получить прямой доступ к переменной-члену num. Затем добавьте метод в GetNumVisitor для возврата значения.

class Element { 
  ...
  friend GetNumVisitor;  // declare GetNumVisitor as a friend class
  ...
};

GetNumVisitor : public Visitor {
 private: 
  int  m_numelement;
 public : 
  void visit(Element *E) { m_elementNum = E->num; }
  int getNum() const { return m_elementNum;}
};

Вам придется называть это как

 ElementA element_a();
 ElementB element_b();
 GetNumVisitor getnumVisitor();
 element_a.accept(getnumVisitor);
 int a = getnumVisitor.getNum();
 element_b.accept(getNumVisitor);
 int b = getnumVisitor.getNum();
 ...
person FKaria    schedule 05.04.2013
comment
объявление GetNumVisitor другом Element приведет к перекомпиляции Element и, следовательно, любого класса, использующего Element, то есть всей иерархии. Вы также можете добавить метод getNum() к Element, технически это будет иметь те же последствия с точки зрения зависимостей компиляции. - person Arne Mertz; 05.04.2013

Не разбираюсь в шаблонах посетителей, но соответствует ли это вашим требованиям?

    class Element{ public : virtual void Accept(Visitor&) = 0
             protected : Element();   int num;
      };

     class ElementA : public Element{
             public : ElementA(); 
             void Accept(Visitor& v) {v.setNum(this->num); v.Visit(this);}
     };

     class ElementB : public Element{
             public : ElementB(); 
             void Accept(Visitor& v) {v.setNum(this->num); v.Visit(this);}


     class Visitor{
             public:
             void Visit(ElementA*);
             void Visit(ElementB*);
             void setNum(int _num) {num = _num;}
             int getNum() {return num;}

             private:
             int num;
     };
person maditya    schedule 05.04.2013
comment
Ему не разрешено перекомпилировать иерархию Element*! - person Y.H.; 05.04.2013
comment
Извините, я почему-то думал, что модифицировать интерфейс нельзя. В моем решении, если функции Accept реализованы в .cpp, он может обойтись без изменения интерфейса, хотя вы правы, что ему придется перекомпилировать. - person maditya; 05.04.2013
comment
Проблема, с которой у меня возникли проблемы, заключается в том, что num защищен ... как вы должны получить к нему доступ из Посетителя? Либо вам нужно добавить getNum в класс Element, либо вам нужно сделать Visitor другом Element - оба из них требуют перекомпиляции... - person maditya; 05.04.2013
comment
Я могу думать только о переинтерпретации указателя, которая не рекомендуется и, вероятно, приведет к неопределенному поведению. Так что, возможно, ответ в том, что это просто невозможно. - person Y.H.; 05.04.2013

Ваш вопрос заключается в том, как разрешить классу (Visitor) прямой доступ к частному элементу данных, унаследованному в другой иерархии классов (Elements).

Быстрый ответ будет: Не надо! это нарушит всю цель модификаторов видимости. Однако это все еще возможно с использованием friend (приводит к перекомпиляции иерархии) или с помощью переинтерпретации указателей. .

Чистым решением было бы использовать шаблон адаптера (используя наследование) для дублирования всей иерархии Elements в другая иерархия-заглушка, которая предоставляет точно такой же интерфейс с делегатами для соответствующих функций, предоставляет функцию получения для num и использует эту новую иерархию с вашим посетителем, который сможет получить доступ к num через получатель.

person Y.H.    schedule 05.04.2013
comment
Переинтерпретация указателей для доступа к закрытым членам — это UB — чистое зло, хотя это будет работать во многих системах. Подружиться с каким-то другим классом было бы излишне тесной связью и привести к перекомпиляции всей иерархии, что недопустимо для OP. Но, к счастью, номер не частный, а защищенный... - person Arne Mertz; 05.04.2013
comment
Я также думаю, что использование переинтерпретации указателей не является хорошей идеей. Однако меня интересует ваше последнее предложение, какая разница в использовании защищенного вместо частного в этом случае? - person Y.H.; 05.04.2013
comment
@Arne, насколько я понимаю, основное различие между частным и защищенным заключается в том, что последний дает производному классу доступ к базовому классу. Но какой в ​​этом смысл, если вам не разрешено изменять классы (т.е. перекомпилировать)? Или я неправильно понимаю эту часть? - person maditya; 05.04.2013
comment
см. мой ответ - вы можете получить от Element создание карманника, который получает доступ к другим элементам ;-) - person Arne Mertz; 05.04.2013
comment
О, это именно то, о чем я подумал после прочтения вашего комментария! преобразование всей иерархии в иерархию-заглушку с использованием закрытого наследования. Проголосуйте! :-) - person Y.H.; 05.04.2013