我正在尝试创建一个将以对象的动态类型调用的重载函数。我尝试这样做,而不会干扰下面的实际类结构,因为我没有直接访问权限(即,我无法添加虚拟方法等)
作为一个具体示例,让我们考虑一下看起来像这样的AST类结构:
class ASTNode {}; // this one is fully abstract; i.e. there's a virtual void method() = 0; class Assignment : ASTNode {}; class Expression : ASTNode {}; class StringExpr : Expression {}; class MathExpr : Expression {};
我想编写一个函数act
,该函数将ASTNode
的一个实例作为参数,并根据其实际的动态类型执行不同的操作。
通话将是这样的
std::shared_ptr<ASTNode> parsedAST = get_a_parsed_ASTNode(); // ... received from some parser or library
act(parsedAST);
然后,我想采取行动,具体取决于ASTNode
的动态类型。
void act(std::shared_ptr<MathExpr> expr) { // Do something with Math expressions, e.g. evaluate their value }; void act(std::shared_ptr<StringExpr> expr) { // Do something with String expressions, e.g. write their value to the log }; void act(std::shared_ptr<Expression> expr) { // do something with other types of expressions (e.g. Boolean expressions) };
但是,由于它们的动态类型可能不是``最具体的类型'',因此目前无法调用。取而代之的是,我必须手动按如下方式手动创建一个调度程序,但是我认为该方法有点愚蠢,因为它实际上除了调度之外什么也没做。
void act(std::shared_ptr<ASTNode> node_ptr) { if(std::shared_ptr<MathExpr> derived_ptr = std::dynamic_pointer_cast<MathExpr>(node_ptr)) { act(derived_ptr); } else if(std::shared_ptr<StringExpr> derived_ptr = std::dynamic_pointer_cast<StringExpr>(node_ptr)) { act(derived_ptr); } else if(std::shared_ptr<Expression> derived_ptr = std::dynamic_pointer_cast<Expression>(node_ptr)) { // do something with generic expressions. Make sure that this is AFTER the more concrete if casts } else if( ... ) // more of this { } // more else if else { // default action or raise invalid argument exception or so... } };
这特别烦人且容易出错,因为我的类层次结构具有许多(> 20)不同的可实例化的具体类。另外,我有各种act
函数,当我重构事物时(例如,将act
添加为其他类型),我必须确保注意{{1}的正确顺序}内。
同样,它也不是那么稳定,因为基础类层次结构的更改将要求我直接更改每个调度程序,而不仅仅是更改特定的if(dynamic_pointer_cast)
函数。
是否有更好/更智能的解决方案?显然,我很喜欢“本机”解决方案,但我也愿意考虑使用库。
答案 0 :(得分:0)
我自己从未遇到过此类问题,但可以想到以下解决方案。
创建一个模仿原始层次结构的层次结构,该层次结构具有虚拟act
,基址具有基本指针,然后将其强制转换为相应的派生指针。
现在,要创建所需的包装,您不需要正确排序的dynamic_cast,只需分配typeid
字符串即可。因此,您的调度是从字符串到包装器工厂的映射。
当然,typeid
字符串需要RTTI,但dynamic_cast
也需要RTTI。