samedi 13 avril 2019

How to implement a fully generic Visitor for a hierarchy of classes in C++1x?

I'd like to implement a fully generic Visitor pattern using >= C++14 using template metaprogramming. I've already found a nice way to generalize the Visitor itself, but I'm having trouble defining the Visitables. The code below works, but I'd like the commented out code in main to work as well; in particular, I want to be able to have a collection of Visitables and apply a Visitor to each element.

Is what I'm trying to do even possible in C++?

Things I've tried:

  • class X : public Visitable<X>
    This solves the problem of not having a suitable accept method in X, but results in ambiguities X/A and X/B which the compiler cannot resolve.
  • empty accept method in X without inheriting; works, but the specialized accept methods in A and B are never called.
  • replace template class Visitor with regular class with function template visit for arbitrary types; does not really change the semantics, but is less readable IMHO
#include <iostream>
#include <vector>

template <typename I>
class Visitable {
 public:
  template <typename Visitor>
  void accept(Visitor&& v) const {
    v.visit(static_cast<const I&>(*this));
  }
};

template <typename T, typename... Ts>
class Visitor : public Visitor<Ts...> {
 public:
  virtual void visit(const T& t);
};

template<typename T>
class Visitor<T> {
 public:
  virtual void visit(const T& t);
};

struct X {
  // template <typename V> void accept(V&& v) const {};
};

struct A : public X, public Visitable<A> {};
struct B : public X, public Visitable<B> {};

class MyVisitor : public Visitor<A, B> {
 public:
  void visit(const A& a) override { std::cout << "Visiting A" << std::endl; }
  void visit(const B& b) override { std::cout << "Visiting B" << std::endl; }
};

int main() {
  MyVisitor v {};
  // std::vector<X> elems { A(), B() };
  // for (const auto& x : elems) {
  //  x.accept(v);
  // }
  A().accept(v);
  B().accept(v);
}

Aucun commentaire:

Enregistrer un commentaire