C++ 在没有派生类的先验知识的情况下,有没有更好的方法来定义访问者模式?
这是我第一次遇到for。在我的场景中,从公共基类派生的对象可以以某种方式相互交互,因此访问者和访问者来自同一个集合。然而,出于实际原因,我的目标是将逻辑与应用程序完全分离。特别是,我希望不惜一切代价避免使用基类,以了解哪些派生类可以使用它。在一个应用程序中,将有不同的独立应用程序层接近相同的基础 问题是需要在基中为每个可能的派生类定义C++ 在没有派生类的先验知识的情况下,有没有更好的方法来定义访问者模式?,c++,templates,design-patterns,C++,Templates,Design Patterns,这是我第一次遇到for。在我的场景中,从公共基类派生的对象可以以某种方式相互交互,因此访问者和访问者来自同一个集合。然而,出于实际原因,我的目标是将逻辑与应用程序完全分离。特别是,我希望不惜一切代价避免使用基类,以了解哪些派生类可以使用它。在一个应用程序中,将有不同的独立应用程序层接近相同的基础 问题是需要在基中为每个可能的派生类定义virtual方法。这可以使用模板轻松解决。一个更大的问题是,我不想被限制在特定数量(或上限)的派生类中。这是我得到的最接近的结果: /*** Generic **
virtual
方法。这可以使用模板轻松解决。一个更大的问题是,我不想被限制在特定数量(或上限)的派生类中。这是我得到的最接近的结果:
/*** Generic ***/
template<class...>
struct Visit { };
template<class... Derived>
struct Base : Visit<Derived...> {
virtual int accept(Base*) = 0;
};
// specializations for different numbers...
template<class X>
struct Visit<X> {
virtual int visit(X*) = 0;
};
template<class X, class Y>
struct Visit<X, Y> {
virtual int visit(X*) = 0;
virtual int visit(Y*) = 0;
};
// and more for 3, 4, ...
/*** Application ***/
struct D1;
struct D2;
using A = Base<D1, D2>; // <--- the goal is to keep this simple
struct D1 : A {
int accept(A* a) { return a->visit(this); }
int visit(D1*) { return 1; }
int visit(D2*) { return 2; }
};
struct D2 : A {
int accept(A* a) { return a->visit(this); }
int visit(D1*) { return 3; }
int visit(D2*) { return 4; }
};
int main() {
A* d1 = new D1();
A* d2 = new D2();
return d2->accept(d1); // expected: 2
}
< >(在代码中完全替换了<代码>访问< /Cord>模板),在任何变化中,C++都是可能的。也就是说,这不起作用,原因与以下原因大致相同:
重载解析仅选择基础模板(或非模板函数,如果有)。只有在决定要选择哪个基础模板,并且该选择被锁定之后,编译器才会四处查看该模板是否有合适的专用化,以及是否会使用该专用化
由于每个注入的visit(X*)
都来自InjectVisit
的不同模板实例化,因此它们不会相互竞争,从而导致歧义错误(即使在任何时候只能使用其中一个)
我试图调整的下半部分,但如果D1
和D2
需要从同一个基派生,则它将不起作用(除非所有派生的都硬编码到该基中)。当然,可以进行dynamic_cast
。但这段代码应该每秒调用100000次,我不希望RTTI成为我的主要瓶颈
目前,我一直停留在一种中间路线上,base
的基类被单个模板类所取代,该模板类需要按照Visit
的思路,由每个应用程序模式分别提供,这似乎是最无害的,但我仍然很好奇。只是列出几个类的名字,并让C++为我生成几行代码真的是不可能的?
由于每个注入的visit(X*)
都来自InjectVisit
的不同模板实例化,因此它们不会相互竞争,从而导致歧义错误(即使在任何时候只能使用其中一个)
您可以使用以下技巧:
我无法测试此ATM,但如果您尝试使用x,y
版本而不是template struct-Visit:Visit{virtual int-Visit(x*)=0;}
,该怎么办。这只是我所问的参数包建议的另一个版本(使用递归而不是扇出),失败时出现了相同的错误。如果您避免专门化,那么?@Amit抱歉,应该更清楚一些。InjectVisit
完全取代了以前的Visit
模板。这不是另一个专业。我会把它放在问题里。太棒了!只有invite
方法消失了(所以现在它只是一个分派),但我能够将它放在Base
的超类中,现在我的模式按预期工作。我能做这个小编辑吗?真的很优雅。我尝试在不同阶段使用
插入,但没有想到在整个过程中都采用这些方法。@当然,继续编辑。这个using
技巧用于实现我所做的更改,希望它们不会破坏您的编码或命名风格。我还将invite
重命名为accept
(wrt.the question),这在访问者模式中更为常见。这种方式说明了如何动态识别指向基类的指针,以在参数和This
中指向D1
或D2
的实例,而不仅仅是在参数中。
template<class X>
struct InjectVisit {
virtual int visit(X*) = 0;
};
template<class... Derived>
struct Base : InjectVisit<Derived>... {
virtual int accept(Base*) = 0;
};
#include <iostream>
void println(const char *s)
{
using namespace std;
cout << s << endl;
}
template<typename X>
struct InjectVisit
{
virtual void visit(X*) = 0;
};
template<typename Head, typename ...Tail>
struct VirtualChain : InjectVisit<Head>, VirtualChain<Tail...>
{
using InjectVisit<Head>::visit;
using VirtualChain<Tail...>::visit;
};
template<typename Head>
struct VirtualChain<Head> : InjectVisit<Head>
{
using InjectVisit<Head>::visit;
};
template<typename ...List>
struct Base : VirtualChain<List...>
{
virtual void accept(Base*) = 0;
};
/****************************************************************/
struct D1;
struct D2;
using ConcreteBase = Base<D1, D2>;
struct D1 : ConcreteBase
{
virtual void accept(ConcreteBase* visitor) { visitor->visit(this); }
virtual void visit(D1*) { println("D1 visited by D1"); }
virtual void visit(D2*) { println("D2 visited by D1"); }
};
struct D2 : ConcreteBase
{
virtual void accept(ConcreteBase* visitor) { visitor->visit(this); }
virtual void visit(D1*) { println("D1 visited by D2"); }
virtual void visit(D2*) { println("D2 visited by D2"); }
};
int main()
{
ConcreteBase* d1 = new D1();
ConcreteBase* d2 = new D2();
d1->accept(d2);
d2->accept(d2);
}
D1 visited by D2
D2 visited by D2