在 C++ 中向多态树添加功能

adding functionality onto polymorphic tree in c++

我有一个多态树,我正在尝试添加功能,例如比较两个节点而不使用像 dynamic_cast 这样的 RTTI,我采用的方法是访问者模式。

我遇到的问题是访问者模式不允许我操作任何参数或从函数中获取 return 类型。

例如,如果我想编写一个比较两个节点的访问者

class AbstractDispatch{
    public:
        virtual void visit(NodeFoo &operand) = 0;
        virtual void visit(NodeBar &operand) = 0;
        //...for all types.
};

class CompareVisitor{
    public: 
        void visit(NodeFoo &operand) override;
        //...
};

class SetVisitor{
    public: 
        void visit(NodeFoo &operand) override;
        //...
};

void CompareVisitor::visit(NodeFoo &operand){
    //compare operand to what?
    //get result of comparison how?
}

void SetVisitor::visit(NodeFoo &operand){
    //set operand to what?
}

我目前的想法是给访问者添加其他功能和成员类。这会让我做这样的事情:

Base *object = new NodeFoo();
CompareVisitor compare;
compare.set_parameters(NodeFoo(/* */));
object->accept(compare);
bool result = compare.get_result();

我可以设置比较访问者的参数并遍历树,检查节点并以这种方式执行其他此类操作。

另一种解决方案是在节点中存储节点类型信息并执行 get_type() 检查是否安全。

dynamic_cast 很慢,但如果节点类型层次结构非常简单,它会更快吗?有没有更好的设计模式来做这样的事情?

您可以编写一个访问者,将当前节点与前一个节点进行比较。

class Node{...}
class NodeFoo : public Node {...}
class NodeBar : public Node {...}

class visitor{
  public:
    void visit( const NodeFoo& node ){
      //First node: store relevant comparison information to private variables
      if( foo == nullptr ){
        foo = &node;
      }
      //Other nodes: Compare to the stored data and store comparison result
      else {
        ...
      }
    }
    void visit( const NodeBar& node ){
      ...
    }
    bool result() const{ return result; };
  private:
    bool result = false;
    //variables for comparison, could be this simple but also a variant type
    // or plain Node*
    NodeFoo* foo = nullptr;
    NodeBar* bar = nullptr;
}

你会像这样使用它

Node node1;
Node node2;
Visitor v;
node1.accept( v );
node2.accept( v );
v.result();

当然,这是一个非常基本的实现,如果您假设所有访问过的节点都具有相同的类型,您可以使用普通的 Node* 来存储第一个节点。您还可以使用变体类型或将类型存储为字符串...(您可以通过执行的访问函数知道类型)

如果节点生命周期不确定,您可以存储一些节点相关数据,您需要比较而不是指向节点的指针....有数百种可能性,这只是一个小草图基本框架

如果允许 CompareVisitor 有一个状态,可以用双 visit(= 两个虚拟调用)来完成。考虑到您的访客 API.

,我认为没有解决方法
#include <iostream>
#include <string>
#include <vector>
#include <memory>

struct FooNode;
struct BarNode;

struct Visitor
{
    virtual void visit(FooNode&)=0;
    virtual void visit(BarNode&)=0;
};
struct Node{
    virtual void accept(Visitor& v) = 0;
};
struct FooNode: public Node{
    virtual void accept(Visitor& v) override { v.visit(*this);}
    const char* print(){return "FooNode";}    
};
struct BarNode: public Node{
    virtual void accept(Visitor& v) override { v.visit(*this);}    
    const char* print(){return "BarNode";}    

};

using ret_type=std::string;
//Feel free to specialize or overload
//Or create comparator class that allows partial specializations
template<typename Left, typename Right>
ret_type compare(Left &left, Right& right){

    return std::string(left.print()) + "<=>" + right.print() + '\n';
}

//Compares (visited) and (rightNode)
class RightCompareVisitor : public Visitor {
public:
    RightCompareVisitor(Node& right):rightNode(right){}
    void visit(FooNode &left) override
    {
        visitRightNode(left);
    }
    void visit(BarNode &left) override
    {
        visitRightNode(left);
    }
    ret_type getRes() { return std::move(result);}
private:
    template<typename Left>
    void visitRightNode(Left& left){
        struct CompareVisitor: Visitor
        {
            ret_type& result;
            Left& left;

            CompareVisitor(ret_type& result, Left& left):result(result), left(left){}

            void visit(FooNode &right) override final{
                result = compare(left, right);
            }
            void visit(BarNode &right) override final{
                result = compare(left, right);                  
            }        
        };
        CompareVisitor v(result, left);
        rightNode.accept(v);
    }
    ret_type result;
    Node& rightNode;
};

//If you add this then you can always just use 'compare' to compare any two 
//nodes.
template<>
ret_type compare<Node,Node>(Node& left, Node& right){
    RightCompareVisitor rC{right};
        left.accept(rC);
    return rC.getRes();
}

int main()
{

    std::vector<std::unique_ptr<Node>> nodes;
    nodes.emplace_back(std::make_unique<FooNode>());
    nodes.emplace_back(std::make_unique<BarNode>());
    nodes.emplace_back(std::make_unique<FooNode>());

    for(auto&& left : nodes)
        for(auto&& right: nodes)
            std::cout<<compare(*left,*right);
}

在你想要的地方添加 consts。如果您希望 RightCompareVisitor 被重用,则对节点使用指针。

输出:

FooNode<=>FooNode    
FooNode<=>BarNode    
FooNode<=>FooNode    
BarNode<=>FooNode    
BarNode<=>BarNode    
BarNode<=>FooNode    
FooNode<=>FooNode    
FooNode<=>BarNode    
FooNode<=>FooNode