gpt4 book ai didi

c++ - 在没有派生类先验知识的情况下,有没有更好的方法来定义访问者模式?

转载 作者:太空狗 更新时间:2023-10-29 22:58:40 25 4
gpt4 key购买 nike

这是我第一次遇到 visitor design pattern对于double dispatch .在我的场景中,派生自公共(public)基类的对象可以以某种方式相互交互,因此访问者和被访问者来自同一个集合。但是,出于实际原因,我的目标是将逻辑应用程序 完全分开。特别是,我想不惜一切代价避免让基类知道哪些派生类可以使用它。一个应用内会有不同的独立应用层接近同一个基础。

问题是需要在基类中为每个可能的派生类定义一个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
}

这有效并满足除最后一个以外的大多数标准。需要提前知道可能的派生类的最大数量,并在 Visit 模板中进行硬编码。仅仅使用不同数量的行重复相同的样板文件很多次并不是很优雅。

我想知道是否有一些更干净的东西

template<class X>
struct InjectVisit {
virtual int visit(X*) = 0;
};

template<class... Derived>
struct Base : InjectVisit<Derived>... {
virtual int accept(Base*) = 0;
};

(完全替换 Visit 模板)在 C++ 中完全可能,在任何变体中。也就是说,这不起作用,原因与 partial specialization of function templates won't 的原因大致相同。 :

Overload resolution only selects a base template (or a nontemplate function, if one is available). Only after it's been decided which base template is going to be selected, and that choice is locked in, will the compiler look around to see if there happens to be a suitable specialization of that template available, and if so that specialization will get used.

由于每个注入(inject)的 visit(X*) 来自 InjectVisit 的不同模板实例化,它们不会相互竞争,导致歧义错误(即使在任何时候只能使用其中一个)。

我尝试改编this answer的后半部分但如果D1D2 需要从相同 基派生(除非,同样,所有派生都是硬-编码到那个)。当然,dynamic_cast 是可能的。但是这段代码每秒要调用几十万次,我不希望 RTTI 成为我的主要瓶颈。

目前我坚持使用一种中间方法,其中 Base 的基类被需要提供的单个模板类替换,沿着 Visit 的行, 分别按每个应用程序模式,这似乎是最不邪恶的,但我仍然很好奇。仅仅列出几个类的名称并让 C++ 按需为我生成几行真的不可能吗?

最佳答案

Since each of the injected visit(X*) comes from a different template instantiation of InjectVisit, they won't compete against each other, leading to ambiguity errors (even though only exactly one of them could be used at any point).

您可以使用以下 using 技巧:

LIVE DEMO

#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

关于c++ - 在没有派生类先验知识的情况下,有没有更好的方法来定义访问者模式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39906738/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com