- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
这是我第一次遇到 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的后半部分但如果D1
和D2
需要从相同 基派生(除非,同样,所有派生都是硬-编码到那个)。当然,dynamic_cast
是可能的。但是这段代码每秒要调用几十万次,我不希望 RTTI 成为我的主要瓶颈。
目前我坚持使用一种中间方法,其中 Base
的基类被需要提供的单个模板类替换,沿着 Visit
的行, 分别按每个应用程序模式,这似乎是最不邪恶的,但我仍然很好奇。仅仅列出几个类的名称并让 C++ 按需为我生成几行真的不可能吗?
最佳答案
Since each of the injected
visit(X*)
comes from a different template instantiation ofInjectVisit
, 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
技巧:
#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/
积累和总结,是长期持续的过程 01 最近,很多朋友微信私聊关于「 butte-java-note 」仓库的话题; 这个「 Git仓库 」每年都会
我即将参加挑战测试,所以我不必参加数据库处理类(class)。尽管在过去的 5 年里我一直在使用数据库,但我还是忍不住对测试感到紧张。这是 50 个问题,有 2 部分:真/假部分和我实际编写 SQL
我的 groovy 代码将 Rabbit Native Plugin 用于 grails: def handleMessage(def body, MessageContext context) {
我想看看是否有人可以就我在 .NET 环境中的进一步知识提供任何建议... 这里有一点背景。我上了一所大学并获得了计算机科学学士学位(主要从事 C、Java 和 C++ 方面的工作)。大学毕业后在一家
我在 postgres 数据库中有一个表,该表用于测试环境,我们需要一次添加和删除多个列。问题是 postgres 最多有 1600 列,并且这个计数包括丢弃的列。我的表永远不会有 1600 个“未丢
作为业余程序员 3 年(主要是 Python 和 C)并且从未编写过超过 500 行代码的应用程序,我发现自己面临两个选择: (1) 学习数据结构和算法设计的基本知识,使我成为一名 l33t 计算机科
有人能告诉我为什么 Android 工作需要 Linux 知识吗?许多 Android 工作都以 Linux 作为先决条件。我可以很好地从 Windows 机器开发 Android 应用程序吗? 最佳
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
关闭。这个问题是off-topic .它目前不接受答案。 想改进这个问题吗? Update the question所以它是on-topic用于堆栈溢出。 关闭 10 年前。 Improve thi
是否可以在 Drools 中保持知识 session ?如果是这样,如何? 我将事实存储在数据库中,并且每次添加新事实时,我都希望避免在新 session 中重新加载所有事实。 目前,当有新事实时,该
我对 C++ 有很好的了解,但从未深入研究 STL。我必须学习 STL 的哪一部分才能提高工作效率并减少工作中的缺陷? 谢谢。 最佳答案 I have good knowledge of C++ 恕我
关闭。这个问题是opinion-based .它目前不接受答案。 想要改进这个问题? 更新问题,以便 editing this post 可以用事实和引用来回答它. 关闭 9 年前。 Improve
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,
关闭。这个问题是opinion-based .它目前不接受答案。 想要改进这个问题? 更新问题,以便 editing this post 可以用事实和引用来回答它. 关闭 7 年前。 Improve
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
在我从 SO answers here 和许多 BackBoneJs 示例中选择的示例之一中,我看到初始化函数知道模型将使用哪个 View 进行渲染。我不知道我现在有点偏见,这是一个好的做法还是取决于
关闭。这个问题是off-topic .它目前不接受答案。 想改进这个问题吗? Update the question所以它是on-topic用于堆栈溢出。 关闭 12 年前。 Improve thi
我在我的网站上使用 C# 和 ASP.NET MVC 3 实现 OpenID 和 OAuth。我基于 DotNetOpenAuth用于后端和openid-selector对于前端。 我喜欢 openi
很长一段时间以来,我都在思考和研究C语言编译器以汇编形式的输出,以及CPU架构。我知道这对你来说可能很愚蠢,但在我看来有些东西是非常无效的。如果我错了,请不要生气,我不明白所有这些原则是有原因的。如果
我有一些 Python 知识,但我从来不认为自己对这门语言特别流利。我正在开发一个潜在的机器视觉项目,该项目将从 SimpleCV 中受益匪浅,但从时间的角度来看,我宁愿不必非常流利地使用 pytho
我是一名优秀的程序员,十分优秀!