首先,对不起这个神秘的头衔,这很难解释。

我想做的是使用重载函数在C++中实现Visitor模式。这是我的情况:

  • 我有一个解析器集合。每个解析器都返回Element的特定派生类型。基本上,我最终得到了Element的多态集合,它们全部实现了visit(Visitor&)函数。
  • 我有一些分析器(Visitor)。每个访问者仅对几个特定的​​Element派生类感兴趣。
  • Visitor的基类有一个空的visit(Element&)实现,该实现将Element引用作为自变量。
  • 每个Visitor派生类都为其感兴趣的特定元素类型实现visit函数。也就是说,在visit(DerivedElement&)类中有一个DerivedVisitor重载函数。
  • 调用accept(Visitor& v) { v.visit(*this); }时,所调用的函数应该是更具体的函数。也就是说,如果vDerivedVisitor,并且accept是在DerivedElement中实现的,则希望调用visit(DerivedElement&)函数。

  • 一些示例代码:
    #include <iostream>
    
    using namespace std;
    
    class Visitor
    {
        public:
            virtual void visit(class BaseElement& e);
    };
    
    class BaseElement
    {
        public:
            virtual void accept(Visitor &v)
            {
                cout << "accept on BaseElement" << endl;
                v.visit(*this);
            }
    
            virtual void doThings()
            {
                cout << "doThings on BaseElement" << endl;
            }
    };
    
    void Visitor::visit(BaseElement& e)
    {
        cout << "visit on Visitor" << endl;
        e.doThings();
    }
    
    class DerivedElement : public BaseElement
    {
        public:
            virtual void accept(Visitor &v)
            {
                cout << "accept on DerivedElement" << endl;
                v.visit(*this);
            }
    
            virtual void doThings()
            {
                cout << "doThings on DerivedElement" << endl;
            }
    };
    
    class DerivedVisitor : public Visitor
    {
        public:
            void visit(BaseElement& e)
            {
                cout << "visit-BaseElement on DerivedVisitor" << endl;
                e.doThings();
            }
    
            void visit(DerivedElement &e)
            {
                cout << "visit-DerivedElement on DerivedVisitor" << endl;
                e.doThings();
            }
    };
    
    int main(int argc, char** argv)
    {
        BaseElement eBase;
        DerivedElement eDeriv;
        BaseElement& eDerivAsBase = eDeriv;
        Visitor vBase;
        DerivedVisitor vDeriv;
    
        cout << "Visiting a BaseElement with the base visitor:" << endl;
        eBase.accept(vBase);
        cout << endl << "Visiting a BaseElement with the derived visitor:" << endl;
        eBase.accept(vDeriv);
    
        cout << endl << "Visiting Base and Derived elements with the derived visitor" << endl;
        eBase.accept(vDeriv);
        eDeriv.accept(vDeriv);
    
        cout << endl << "Visiting Base element as Derived reference" << endl;
        eDerivAsBase.accept(vBase);
        eDerivAsBase.accept(vDeriv);
    
    }
    

    这是输出
    Visiting a BaseElement with the base visitor:
    accept on BaseElement
    visit on Visitor
    doThings on BaseElement
    
    Visiting a BaseElement with the derived visitor:
    accept on BaseElement
    visit-BaseElement on DerivedVisitor
    doThings on BaseElement
    
    Visiting Base and Derived elements with the derived visitor
    accept on BaseElement
    visit-BaseElement on DerivedVisitor
    doThings on BaseElement
    accept on DerivedElement
    visit-BaseElement on DerivedVisitor (!)
    doThings on DerivedElement
    
    Visiting Base element as Derived reference
    accept on DerivedElement
    visit on Visitor
    doThings on DerivedElement
    accept on DerivedElement
    visit-BaseElement on DerivedVisitor (!)
    doThings on DerivedElement
    

    标有(!)的行是我要更改的行。这些行应为“DerivedVisitor上的visit-DerivedElement”。

    这可能吗?看到C++没有实现multiple dispatch似乎很困难,我可能正在寻求不可能的实现。但是,我真的很想看看我有什么选择,因为为我拥有的每个派生元素编写空的accept(DerivedElementN&)方法似乎不是最佳选择。

    最佳答案

    您在这里做了很多动态间接。因此,您也将需要以这种方式构造访客。

     struct BaseVisitor {
         std::unordered_map<std::type_info, std::function<void(BaseElement&)>> types;
         template<typename D, typename F> void AddOverload(F f) {
             types[typeid(D)] = [=](BaseElement& elem) {
                 f(static_cast<D&>(elem));
             };
         }
         virtual void visit(BaseElement& elem) {
             if (types.find(typeid(elem)) != types.end())
                 types[typeid(elem)](elem);
         }
     };
     struct DerivedVisitor : BaseVisitor {
         DerivedVisitor() {
             AddOverload<DerivedElement>([](DerivedElement& e) {
             });
             //... etc
         }
     };
    

    核心问题是,只要需要这种动态间接,就不能使用模板。您所能做的就是在垃圾邮件dynamic_cast上提供一层额外的类型安全性和便利性(并可能提高速度)。

    简要说明一下,上面的代码可能无法正常工作-使用typeid来引用或const或从某些东西中获取乐趣,可能会导致类型查找在应成功时失败。

    如果对您很重要,则可以应用其他技术来消除该限制,但是您可能要坚持使用dynamic_cast,因为它很有趣但很糟糕。

    关于c++ - 从对基类的引用中调用更具体的重载函数,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/29565524/

    10-11 20:22