C++ 从对基类的引用调用更具体的重载函数

C++ 从对基类的引用调用更具体的重载函数,c++,overriding,overload-resolution,visitor,C++,Overriding,Overload Resolution,Visitor,首先,很抱歉这个神秘的标题,这不容易解释 我想做的是用超载函数在C++中实现访问者模式。这就是我的情况: 我有一个解析器集合。每个解析器返回一个特定的派生类型的元素。基本上,我最终得到了元素的多态集合s,它们都实现了visit(Visitor&)功能 我有一些分析器(Visitors)。每个访问者只对几个特定的元素派生类感兴趣 Visitor的基类有一个空的visit(Element&)实现,它接收一个元素引用作为参数 每个Visitor派生类为其感兴趣的特定元素类型实现visit函数。也就是说

首先,很抱歉这个神秘的标题,这不容易解释

我想做的是用超载函数在C++中实现访问者模式。这就是我的情况:

  • 我有一个解析器集合。每个解析器返回一个特定的派生类型的
    元素
    。基本上,我最终得到了
    元素的多态集合
    s,它们都实现了
    visit(Visitor&)
    功能
  • 我有一些分析器(
    Visitor
    s)。每个访问者只对几个特定的
    元素
    派生类感兴趣
  • Visitor
    的基类有一个空的
    visit(Element&)
    实现,它接收一个
    元素
    引用作为参数
  • 每个
    Visitor
    派生类为其感兴趣的特定元素类型实现
    visit
    函数。也就是说,我在
    DerivedVisitor
    类中有一个
    visit(DerivedElement&)
    重载函数
  • 调用
    accept(Visitor&v){v.visit(*this);}
    时,所调用的函数应该是更具体的函数。也就是说,如果
    v
    DerivedVisitor
    并且
    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);
    
    }
    
    标有(!)的行是我想更改的行。这些行应该是“在DerivedVisitor上访问DerivedElement”


    这可能吗?看到C++没有实现它,我似乎很难做到。然而,我真的很想看看我有什么选择,因为为我拥有的每个派生元素编写空的
    accept(DerivedElementN&)
    方法似乎不是最好的选择。

    建议

    将文章标题改为“如何实现访问者模式的专门化”

    注意: 您忘记在
    DerivedVisitor
    类中使用
    virtual
    关键字

    回答

    您使用的两个功能混合不好:

    方法重写(“虚拟”)。其中,来自相关类且id相同的方法具有相同的参数

    class Visitor
    {
        public:
            virtual void visit(BaseElement& e)
            {
              // print element value to console
            }
    };
    
    class DerivedVisitor : public Visitor
    {
        public:
            virtual void visit(BaseElement& e)
            {
              // save element value into file
            }
    };
    
    class Visitor
    {
        public:
            void visit(BaseElement& e)
            {
              // ...
            }
    };
    
    class DerivedVisitor : public Visitor
    {
        public:
            void visit(BaseElement& e)
            {
              // ...
            }
    
            void visit(DerivedElement& e)
            {
              // ...
            }
    };
    
    方法重载。其中,来自相关类且id相同的方法具有不同的参数

    class Visitor
    {
        public:
            virtual void visit(BaseElement& e)
            {
              // print element value to console
            }
    };
    
    class DerivedVisitor : public Visitor
    {
        public:
            virtual void visit(BaseElement& e)
            {
              // save element value into file
            }
    };
    
    class Visitor
    {
        public:
            void visit(BaseElement& e)
            {
              // ...
            }
    };
    
    class DerivedVisitor : public Visitor
    {
        public:
            void visit(BaseElement& e)
            {
              // ...
            }
    
            void visit(DerivedElement& e)
            {
              // ...
            }
    };
    
    由于您没有显示足够的源代码,因此我假设您将参数作为
    元素传递&
    ,即使它是作为
    DerivedElement&
    创建的,因此编译器调用
    void-visit(BaseElement&e)
    ,而不是
    void-visit(DerivedElement&e)

    我还注意到,您使用的是“引用”,而不是指针。编译器可能会将您的
    DerivedElement
    强制转换为
    BaseElement
    。我建议用“指针”来代替

    在使用多态性时,最好不要使用“方法重载”,并坚持使用“方法重写”,因为编译器总是调用“虚拟”或“重写”方法

    类基本元素
    {
    公众:
    虚拟无效接受(访客和验证)
    {
    
    cout您在这里做了大量的动态间接操作。因此,您也需要以这种方式构建您的访问者

     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
         }
     };
    
    struct BaseVisitor{
    std::无序的_映射类型;
    模板无效添加重载(F){
    类型[typeid(D)]=[=](基本元素和元素){
    f(静态_-cast(elem));
    };
    }
    虚拟无效访问(BaseElement和elem){
    if(types.find(typeid(elem))!=types.end()
    类型[类型ID(元素)](元素);
    }
    }; 
    结构派生访问者:BaseVisitor{
    DerivedVisitor(){
    AddOverload([](派生元素和e){
    });
    //…等等
    }
    };
    
    核心问题是,只要你需要动态间接寻址,你就不能使用模板。你所能做的就是在垃圾邮件
    动态\u cast
    上提供额外的一层类型安全和方便(以及潜在的速度)

    作为一个简短的说明,上面的代码可能不会很好地工作-typeid对引用或
    const
    或类似的东西有一些乐趣,它们可能会导致类型查找在成功时失败


    如果对您来说很重要的话,还可以应用其他技术来消除这一限制,但您可能希望坚持使用dynamic_cast,因为它很有趣但很糟糕。

    问题是,这与我提出的不同。我需要您的示例没有提供的专门化。我想添加一个不同的实现n
    DerivedVisitor
    用于
    DerivedElements
    s,但这不起作用。此外,问题是
    visit
    元素不是从
    main
    函数中调用的,而是从
    BaseElement/DerivedElement
    类中的
    accept
    方法中调用的,这增加了更多的难度。@gjulianm检查对我答案的更新。每个
    访问者只有一个
    visit
    方法,
    accept
    方法是虚拟的,并且在每个派生类或专门化类中都“重写”。但是它仍然有相同的问题。如果调用
    e1->accept(*v)
    ,当
    accept
    接收到
    BaseVisitor
    参数时,它将只调用接收
    BaseElement
    的常规
    visit
    方法
    方法在
    DerivedVisitor
    类中,它不会从
    accept
    mehod调用,这就是为什么。也就是说,我实际上想混合方法重载和重写。@Gjulian对不起,你真的像示例那样修改和编译了吗,或者你支持不起作用吗?我会把这个示例带到我的家用电脑上,在多态性中,如果将参数传递给cl