C++ позволяет избежать дублирования кода для константных и неконстантных посещений

У меня есть класс, который должен вызывать метод посетителя для каждой переменной-члена. Что-то вроде этого:

class A{
    int a, b, c;

public:
    void accept(Visitor &visitor){
        visitor.visit(a);
        visitor.visit(b);
        visitor.visit(c);
    }
};

Как я могу получить метод void accept() const с тем же кодом без дублирования кода?

Очевидным решением с дублированием является добавление метода:

void accept(Visitor &visitor) const {
    visitor.visit(a);
    visitor.visit(b);
    visitor.visit(c);
}

Этот метод имеет именно то значение, которое мне нужно, но я хотел бы избежать дублирования кода. Причина использования обоих методов заключается в том, чтобы иметь возможность читать переменные «читающим» посетителем и иметь accept метод const. Тогда неконстантный accept можно будет использовать для «записи/обновления» посетителей.


person Juraj Blaho    schedule 09.03.2011    source источник
comment
Где происходит дублирование кода. Недостаточно информации.   -  person Captain Giraffe    schedule 10.03.2011
comment
Какая подпись у visit? Если это константа, то нет необходимости в неконстантной accept. Если это не так, то это accept не может быть константой.   -  person Beta    schedule 10.03.2011
comment
Разве вы не можете просто поставить const на текущий метод accept? Зачем вам нужна неконстантная версия accept?   -  person Tim    schedule 10.03.2011
comment
@Beta: возможно, что visit() перегружен для const и не-const.   -  person Oliver Charlesworth    schedule 10.03.2011
comment
@Beta: Visitor::visit может быть перегружен для параметра const int& и int&, причем последний потенциально может изменить посещаемую вещь. Не уверен, что это мудро, но текущий дублированный код спрашивающего позволяет это сделать.   -  person Steve Jessop    schedule 10.03.2011
comment
@ Оли, но между accept(...) const и accept(const Visitor&) есть большая разница. С неконстантным посетителем мы бы вызвали неконстантный Visitor::visit() - независимо от того, является ли accept() константным или неконстантным.   -  person Tim    schedule 10.03.2011
comment
@Tim: я больше думал о Visitor::visit(const int &) и Visitor::visit(int &).   -  person Oliver Charlesworth    schedule 10.03.2011
comment
@Oli и @Steve - хорошее замечание, но фу. Я очень надеюсь, что Visitor не такой.   -  person Tim    schedule 10.03.2011
comment
Это не дублирование кода, если функции вызывают другую перегрузку посещения. Если бы они назывались Visitor.read и Visitor.update, мы бы это не рассматривали. Зачем беспокоиться о том, что у них одно и то же имя?   -  person Bo Persson    schedule 10.03.2011
comment
@Bo: Даже если у методов разные имена, я бы посчитал это дублированием кода.   -  person Juraj Blaho    schedule 10.03.2011
comment
@Bo: меня беспокоит то, что некоторые стили предпочитают не проходить по неконстантной ссылке, особенно со встроенными типами и особенно с именами функций, которые явно не предполагают модификации. Это означает, что некоторые программисты будут удивлены, когда visit(a) изменяет this->a, а не передает его по значению. Я могу с этим смириться, тем более что он позволяет простое совместное использование кода для нескольких таких функций посетителя. Именование функций по-другому может по-прежнему работать, если шаблон принимает дополнительный аргумент шаблона указателя на функцию-член, и может помочь некоторым читателям, поэтому я не уверен.   -  person Steve Jessop    schedule 10.03.2011


Ответы (2)


Вы можете создать вспомогательную функцию статического шаблона класса, которая будет выводить константность на основе типа указателя this, который вы ему предоставляете. Как это:

class A{
    int a, b, c;

public:

    void accept(Visitor &visitor){
        acceptImpl(*this, visitor);
    }
    void accept(Visitor &visitor) const{
        acceptImpl(*this, visitor);
    }

private:
    template<typename t_A>
    static void acceptImpl(t_A& aObj, Visitor &visitor)
    {
        visitor.visit(aObj.a);
        visitor.visit(aObj.b);
        visitor.visit(aObj.c);
    }
};
person zdan    schedule 09.03.2011

Помощник по шаблону:

class A{
    int a, b, c;

private:
    template <typename T>
    static void do_visiting(T &self, Visitor &visitor) {
        visitor.visit(self.a);
        visitor.visit(self.b);
        visitor.visit(self.c);
    }
public:
    void accept(Visitor &visitor) {
        do_visiting(*this, visitor); // calls do_visiting<A>
    }
    void accept(Visitor &visitor) const {
        do_visiting(*this, visitor); // calls do_visiting<const A>
    }
};
person Steve Jessop    schedule 09.03.2011