我想使用> = C ++ 14通过模板元编程实现完全通用的Visitor模式。我已经找到了一种很好的方式来概括Visitor本身,但是在定义Visitables时遇到了麻烦。下面的代码可以工作,但是我希望main
中注释掉的代码也可以工作;特别是,我希望能够有一个Visitables集合并将一个Visitor应用于每个元素。
我正在尝试用C ++做到吗?
我尝试过的事情:
class X : public Visitable<X>
accept
方法
X
,但会导致编译器产生歧义X/A
和X/B
无法解决。accept
中的X
方法,无需继承;可以,但是
从未调用accept
和A
中的专用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);
}
答案 0 :(得分:0)
*p=*p+1; // this increments the value
*p++; //while this does not
但是请注意,此处的调度是静态的。您的向量包含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> {};
个而不是X
个或A
个。
您可能想要
B
距离越来越近。
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));
}
};
这仍然不能满足您的要求,因为您有一个值向量。而且多态值需要更多的工作。
将其作为X的唯一ptrs的向量,并添加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> {};
和一些virtual ~X(){}
和*
,这将满足您的要求。
答案 1 :(得分:0)
您当前的解决方案存在一些问题:
A
和B
值正确存储在集合中,从而无法访问集合中的每个元素。 X
不能做到这一点,因为无法要求X
的子类也可以继承Visitable
类模板的实例。vector<A>
或vector<B>
,在这种情况下,您将无法在访问者中存储不同可访问类型的值相同的集合。您要么需要一种在运行时处理 访客/访客不匹配情况的方法,要么需要更加复杂的模板结构。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);
}