Элегантно сравнивая полиморфные деревья в C ++ - PullRequest
0 голосов
/ 05 сентября 2018

У меня есть дерево полиморфных объектов. Мне нужно пройти два дерева и сравнить узлы. Если узлы имеют разные типы, они не равны. Рассмотрим эту иерархию:

struct Visitor;

struct Base {
  virtual ~Base() = default;
  virtual void accept(Visitor &) = 0;
};
using BasePtr = std::unique_ptr<Base>;

struct A final : Base {
  void accept(Visitor &) override;
  int data;
};
struct B final : Base {
  void accept(Visitor &) override;
  BasePtr child;
};
struct C final : Base {
  void accept(Visitor &) override;
  std::vector<BasePtr> children;
};

struct Visitor {
  virtual void visit(const A &) = 0;
  virtual void visit(const B &) = 0;
  virtual void visit(const C &) = 0;
};

Я знаю, как реализовать эти функции:

bool equalNode(const A &, const A &);
bool equalNode(const B &, const B &);
bool equalNode(const C &, const C &);

Я спрашиваю, как мне реализовать эту функцию:

bool equalTree(const Base *, const Base *);

Как мне элегантно перейти от equalTree к equalNode, возможно, используя шаблон посетителя?

Ответы [ 2 ]

0 голосов
/ 05 сентября 2018

Здесь есть три подхода.

<ч />

Первый - вам нужно сделать двойную отправку. У вас есть один посетитель для одной стороны и один посетитель для другой стороны. Выбрав одну сторону, вы «сохраняете» этот тип в качестве параметра шаблона, который можно использовать для перехода на другую сторону:

template <typename T>
struct Right : Visitor {
     Right(T const* lhs) : lhs(lhs) { }
     T const* lhs;
     bool result;

     void visit(A const& x) override { visit_impl(x); }
     void visit(B const& x) override { visit_impl(x); }
     void visit(C const& x) override { visit_impl(x); }

     void visit_impl(T const& x) { result = equalNode(*lhs, x); }
     template <typename U> void visit_impl(U const&) { result = false; }
};

struct Left : Visitor {
    Left(Base const* lhs, Base const* rhs) : rhs(rhs) {
        lhs->accept(*this);
    }
    Base const* rhs;
    bool result;

    void visit(A const& x) override { visit_impl(x); }
    void visit(B const& x) override { visit_impl(x); }
    void visit(C const& x) override { visit_impl(x); }

    template <typename U>
    void visit_impl(U const& lhs) {
        Right<U> right(&lhs);
        rhs->accept(right);
        result = right.result;
    }
};

bool equalTree(const Base *lhs, const Base *rhs) {
    return Left(lhs, rhs).result;
}
<ч />

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

struct Eq : Vistor {
    Eq(Base const* lhs, Base const* rhs) : rhs(rhs) {
        lhs->accept(*this);
    }

    Base const* rhs;
    bool result;

    void visit(A const& x) override { visit_impl(x); }
    void visit(B const& x) override { visit_impl(x); }
    void visit(C const& x) override { visit_impl(x); }

    template <typename U>
    void visit_impl(U const& x) {
        result = equalNode(x, *static_cast<U const*>(rhs));
    }
};

bool equalTree(Base const* lhs, Base const* rhs) {
    if (typeid(*lhs) == typeid(*rhs)) {
        return Eq(lhs, rhs).result;
    } else {
        return false;
    }
}
<ч />

Третье - вы не делаете это через OO и вместо этого используете variant:

using Element = std::variant<A, B, C>;

struct Eq {
    template <typename T>
    bool operator()(T const& lhs, T const& rhs) const {
        return equalNode(lhs, rhs);
    }

    template <typename T, typename U>
    bool operator()(T const&, U const&) const {
        return false;
    }
};

bool equalTree(Element const& lhs, Element const& rhs) {
    return std::visit(Eq{}, lhs, rhs);
}
0 голосов
/ 05 сентября 2018

Что-то вроде

struct RhsVisitor : public Visitor
{
    bool result;
};

struct AEqualVisitor : public RhsVisitor
{
    void visit(const A & rhs) override { result = equalNode(lhs, rhs); }
    void visit(const B &) override { result = false; }
    void visit(const C &) override { result = false; }

    const A & lhs;
};

и аналогичные для B и C

struct LhsVisitor : public Visitor
{
    void visit(const A & a) override { rhsVisitor = std::make_unique<AEqualVisitor>(a); }
    void visit(const B & b) override { rhsVisitor = std::make_unique<BEqualVisitor>(b); }
    void visit(const C & c) override { rhsVisitor = std::make_unique<CEqualVisitor>(c); }

    std::unique_ptr<RhsVisitor> rhsVisitor;
};

bool equalTree(const Base * lhs, const Base * rhs)
{
    LhsVisitor vis;
    lhs->accept(vis);
    rhs->accept(*vis.rhsVisitor);
    return vis.rhsVisitor->result;
};
...