【问题标题】:Circular Dependency Issue in My Visitor Design Pattern我的访客设计模式中的循环依赖问题
【发布时间】:2021-10-06 05:46:24
【问题描述】:

我正在尝试实现访问者设计模式来为解释器构建表达式树。我在头文件中声明所有内容并遇到一个问题,我需要在其他所有内容之前定义访问者,但这是不可能的,因为我的 Expr 类需要在它之前定义访问者,但是 不可能,因为其他类需要Expr 在他们之前定义,我只是不确定该怎么做。

#include "Token.h"

template <typename R>
class Visitor {
        virtual R visitBinaryExpr(Binary expr);
};

class Expr {
public:
template<typename R>
    R accept(Visitor<R> visitor);
};

class Binary : public Expr {
public:
    Binary(Expr left, Token oper, Expr right) : left(left), oper(oper), right(right)    {
    }

    template<typename T>
    T accept(T visitor) {
        return visitor.visitBinaryExpr(this);
    }

    const Expr left;
    const Token oper;
    const Expr right;
};

给出语法错误:第 5 行的标识符“二进制”

这是代码的一个小例子。任何帮助将不胜感激!

【问题讨论】:

标签: c++ circular-dependency circular-reference visitor-pattern


【解决方案1】:

Andrei Alexandrescu 在他的著作《现代 C++ 设计:通用编程和应用设计模式》中展示的访问者模式已经解决了循环引用的问题

他称这种模式为:“非循环访问者”

说实话。你的程序看起来很像他的例子。但无论如何。为此,您可以稍作修改。

我也实现了这样一个通用的访问者here

代码:

// ------------------------------------------------------------------------------------------------------------------------------------------------
// 1. Visitor part

    // You need to derive a Visitor class from VisitorBase. That is mandatory
    class VisitorBase
    {
        public:
            VisitorBase(void) {}
            virtual ~VisitorBase(void) {}
    };


    // Additionally you need to derive your visitor class from this class
    // and as many times as you want to visit specific classes that you must define
    // as template parameters.
    //
    // So a typical visitor class definition looks like
    //
    // class AVisitorForSomeOtherTypes :    public VisitorBase
    //                                      public Visitor<VisitibleType1>,
    //                                      public Visitor<VisitibleType2>,
    //                                      public Visitor<VisitibleTypex>
    //
    // With such a Visitor you can visit the classes specified in the template parameters
    //
    template <class T>
    class Visitor
    {
        public:
            Visitor(void) {}
            virtual ~Visitor(void) {}
            
            virtual void visit(T&) = 0;
    };


// ------------------------------------------------------------------------------------------------------------------------------------------------
// 2. Visitable part

    class VisitableBase
    {
        public:
            VisitableBase(void) {}
            virtual ~VisitableBase() {}
            // Will be overridden by macro
            // If somebody forgets to insert the macro, we will do nothing
            virtual void acceptAGuestVisitor(VisitorBase&) { };
        protected:
        
            // Because the DEFINE_VISITABLE macro will be used to finally call this function
            // Type T, set to "this" of the class where the macro was defined, T will be
            // of the type of the class where the macro was defined
            // This is just a redirector. And it avoids circular forward declarations
            // Hence "Acyclic"
            
            template <class TypeOfClassThatWillBeVisited>
            static void acceptDecoupled(TypeOfClassThatWillBeVisited& visited, VisitorBase* guestVisitorWhoWillVisitSomeOtherClass)
            {
                // Apply the Acyclic Visitor
                // Since Visitor classes are always derived from VisitorBase
                // and specific Visitor <templated> classes, the dynamic cast will 
                // work, if the guest, is also in the list of Base Classes from which 
                // the visitor is derived
                //
                // The visit function will not be called for unknown Visitors
                // in the example above, not for a "VisitibleTypekkk"
                
                if (Visitor<TypeOfClassThatWillBeVisited>* visitorGuest =   dynamic_cast<Visitor< TypeOfClassThatWillBeVisited>*>(guestVisitorWhoWillVisitSomeOtherClass))
                {
                    // Call the Visitors visit function (if existing)
                    visitorGuest->visit(visited);
                }
                return;
            }
    };


// This Macro hast to be defined in the class that shall be visited.
// So, in the visitable class.
// It will define the accept function which is typical for the visitor pattern.
// Accept will then call the "static acceptDecoupled" function, which 
// calls the visit function with "*this" (of the class where the macro was defined)
// as parameter.
#define DEFINE_VISITABLE() virtual void acceptAGuestVisitor(VisitorBase* guestVisitorWhoWillVisitSomeOtherClass) { acceptDecoupled(*this, guestVisitorWhoWillVisitSomeOtherClass); }

【讨论】:

    【解决方案2】:

    嗯,这很快!是的,前向声明是我需要的。我对另一个问题感到困惑,这是由于我没有将我的方法定义为我使用前向声明的某种问题。我的代码现在看起来像,

    #include "Token.h"
    
    class Binary;
    class Grouping;
    class LiteralExpression;
    class Unary;
    
    template <typename R>
    class Visitor {
        virtual R visitBinaryExpr(Binary expr) { R temp; return temp; };
    };
    
    class Expr {
    public:
        template<typename R>
        R accept(Visitor<R> visitor) { R temp; return temp; };
    };
    
    class Binary : public Expr {
    public:
        Binary(Expr left, Token oper, Expr right) : left(left), oper(oper), right(right)    {
        }
    
        template<typename T>
        T accept(T visitor) {
            return visitor.visitBinaryExpr(this);
        }
    
        const Expr left;
        const Token oper;
        const Expr right;
    };
    

    不过,现在我有点担心 accept 方法是否会起作用,或者它是否只会从基 Expr 类中调用我的 temp 方法。哦,那是另一个问题了。感谢您的帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-09-25
      • 2011-01-04
      • 2015-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多