如何在C ++ 1x中为类的层次结构实现完全通用的访问者?

问题描述 投票:0回答:2

我想使用模板元编程使用> = C ++ 14来实现完全通用的访问者模式。我已经找到了一个很好的方法来概括访问者本身,但我在定义Visitables方面遇到了麻烦。下面的代码有效,但我希望main中注释掉的代码能够正常工作;特别是,我希望能够拥有一组Visitables并将访问者应用于每个元素。

我正在尝试用C ++做什么?

我试过的事情:

  • class X : public Visitable<X> 这解决了在accept中没有合适的X方法的问题,但是导致编译器无法解决的模糊性X/AX/B
  • accept中没有继承的空X方法;有效,但acceptA的专业B方法从未被称为。
  • 将模板类Visitor替换为带有函数模板visit的常规类,用于任意类型;并没有真正改变语义,但不太可读恕我直言
#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);
}
c++ templates design-patterns metaprogramming visitor
2个回答
0
投票
struct empty_t{};
template <class I, class B=empty_t>
class Visitable:public B {
public:

// ...

struct X : Visitable<X>{
};

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

但请注意,此处的调度是静态的。而你的载体含有Xs而不是As或Bs。

你可能想要

template <class Visitor>
struct IVisitable {
  virtual void accept(Visitor const& v) const = 0;
protected:
  ~IVisitable(){}
};

template <class I, class Visitor, class B=IVisitable<Visitor>>
struct Visitable {
  virtual void accept(Visitor const& v) const override {
    v.visit(static_cast<const I&>(*this));
  }
};

越来越近了。

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

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

这仍然不能做你想要的,因为你有一个值向量。多态值需要更多的工作。

使它成为X的独特ptrs矢量,并添加virtual ~X(){}和一些*make_uniques,这将做你想要的。


0
投票

您当前的解决方案存在一些问题:

  1. 您没有可以表示任何可访问类型的多态类型。这意味着您无法将所有AB值正确存储在集合中,以便您可以访问集合中的每个元素。 X没有实现这一点,因为没有办法要求X的子类也是Visitable类模板的实例化的子类。
  2. 您无法处理访客/可访问类型的不匹配;您不能保证您的集合中的所有值都可以通过某种访问者类型访问,而不是简单地将集合设置为vector<A>vector<B>,在这种情况下,您将失去在同一集合中存储不同可访问类型的值的能力。您需要一种方法来在运行时处理访问者/可访问不匹配的场景,或者您需要更复杂的模板结构。
  3. 您不能直接在集合中存储多态值。这是因为vector将其元素连续存储在内存中,因此必须为每个元素假定一定的常量大小;就其性质而言,多态值具有未知的大小。解决方案是使用一组(智能)指针来引用堆上其他位置的多态值。

这是您原始代码的工作改编:

#include <iostream>
#include <vector>
#include <memory>

template<typename T>
class Visitor;

class VisitorBase {
public:
    virtual ~VisitorBase() {}
};

class VisitableBase {
public:
    virtual void accept(VisitorBase& v) const = 0;
    virtual ~VisitableBase() {}
};

template <typename I>
class Visitable : public VisitableBase {
public:
    virtual void accept(VisitorBase& v) const {
        auto visitor = dynamic_cast<Visitor<I> *>(&v);
        if (visitor == nullptr) {
            // TODO: handle invalid visitor type here
        } else {
            visitor->visit(dynamic_cast<const I &>(*this));
        }
    }
};

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

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

class MyVisitor : public Visitor<A>, public Visitor<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<std::shared_ptr<VisitableBase>> elems {
        std::dynamic_pointer_cast<VisitableBase>(std::make_shared<A>()),
        std::dynamic_pointer_cast<VisitableBase>(std::make_shared<B>())
    };
    for (const auto& x : elems) {
        x->accept(v);
    }
    A().accept(v);
    B().accept(v);
}
© www.soinside.com 2019 - 2024. All rights reserved.