【发布时间】:2020-06-27 05:38:58
【问题描述】:
我正在尝试创建一个重载函数,该函数将使用对象的动态类型调用。我尝试这样做不干扰下面的实际类结构,因为我没有直接访问权限(即我无法添加虚拟方法等)
作为一个具体的例子,让我们考虑一个看起来有点像这样的 AST 类结构:
类 ASTNode {}; // 这个是完全抽象的;即有一个 virtual void method() = 0;
类分配:ASTNode {};
类表达式:ASTNode {};
类 StringExpr : 表达式 {};
类 MathExpr : 表达式 {};
我想编写一个函数act,它将ASTNode 的实例作为参数,并根据其实际动态类型做一些不同的事情。
调用将是这样的
std::shared_ptr<ASTNode> parsedAST = get_a_parsed_ASTNode(); // ... received from some parser or library
act(parsedAST);
然后,我要行动,取决于ASTNode的动态类型。
void act(std::shared_ptrexpr) { // 用数学表达式做一些事情,例如评估他们的价值 }; 无效行为(std::shared_ptr expr) { // 用字符串表达式做一些事情,例如将它们的值写入日志 }; 无效行为(std::shared_ptr expr) { // 对其他类型的表达式做一些事情(例如布尔表达式) };
目前,我无法调用,因为它们的动态类型可能不是“最具体的类型”。相反,我必须手动创建一个调度程序,如下所示,但我认为该方法有点傻,因为它实际上除了调度之外什么都不做。
void act(std::shared_ptrnode_ptr) { if(std::shared_ptr derived_ptr = std::dynamic_pointer_cast (node_ptr)) { 行为(派生的_ptr); } 否则 if(std::shared_ptr derived_ptr = std::dynamic_pointer_cast (node_ptr)) { 行为(派生的_ptr); } 否则 if(std::shared_ptr derived_ptr = std::dynamic_pointer_cast (node_ptr)) { // 用泛型表达式做一些事情。确保这是在更具体的 if casts 之后 } else if( ... ) // 更多 { } // 更多 else if 别的 { // 默认操作或引发无效参数异常等等... } };
这特别烦人且容易出错,因为我的类层次结构中有许多 (> 20) 可以实例化的不同具体类。此外,我有各种act 函数,当我重构事物时(例如,为其他类型添加act),我必须确保注意调度程序中if(dynamic_pointer_cast) 的正确顺序。
而且它也不是那么稳定,因为底层类层次结构的更改将需要我直接更改每个调度程序,而不仅仅是特定的 act 函数。
是否有更好/更智能的解决方案?显然我很欣赏“原生”解决方案,但我也愿意考虑图书馆。
【问题讨论】:
-
是只有基类不受你控制,还是部分派生类?如果您可以控制所有派生类,则可以使用 CRTP 方法。否则,您需要在代码中的某处拥有类型和类型的顺序。制作一个从列表生成调度函数的模板可能是一个更易于维护的选项。
-
不幸的是,我无法控制整个类层次结构。 CRTP 看起来很有趣,但我认为它不太适合,除非我以某种方式通过创建双重继承结构来“隐藏”整个类层次结构,其中每个“影子”类都继承自 1. 原始类,2. 它是影子超级。然后我可以将我的逻辑添加到影子类中。但是,这仍然给我留下了需要维护的整个影子类层次结构。
标签: c++ c++11 polymorphism dispatch