ここには 3 つのアプローチがあります。
1 つ目は、二重ディスパッチを行う必要があることです。一方の側に 1 人の訪問者がいて、もう一方の側に 1 人の訪問者がいます。一方を選択したら、その型をテンプレート パラメーターとして「保存」します。これを使用して、反対側にアクセスできます。
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;
}
2 つ目は、ごまかすことです。両側が同じタイプの場合のみ気にするので、単一のディスパッチのみが必要です。
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;
}
}
3 つ目は、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);
}