简体   繁体   English

混合模板和非模板访问者方法

[英]Mix template and non-template visitor methods

Currently I am learning about the Visitor Pattern and try out various ideas. 目前,我正在学习访客模式并尝试各种想法。 Below I have the code of my current setup, which I would like to get functioning somehow. 下面有我当前设置的代码,我想以某种方式使其运行。

I would like to have two visitors, one that counts instances of Red and Blu separately and one that counts anything (one can assume, it's a Color ) 我想有两个访问者,一个来分别计算RedBlu实例,另一个来计算任何东西(一个可以假设,它是Color )。

This is of course solvable by simply implementing the second visitor analogously to the first one, however not using separate variables for counting, but just one. 当然,这可以通过简单地实现类似于第一个访客的第二个访客来解决,但是不使用单独的变量进行计数,而只是使用一个变量。 I think however this is unnecessary - if I had for example many, many different colours, the code would be very repetitive: All functions in that visitor would be same, they would simply increment one variable. 但是我认为这是不必要的-例如,如果我有很多不同的颜色,则代码将是非常重复的:该访问者中的所有函数都是相同的,它们只会增加一个变量。 Surely, there is an easier way, but how? 当然,有一种更简单的方法,但是如何? According to the standard Visitor Pattern I have to implement for every color class a visit functions, thus this does not seem to be the right approach. 根据标准的访问者图案,我必须为每种颜色类别实现访问功能,因此这似乎不是正确的方法。

How would someone solve this problem? 有人将如何解决这个问题?

#include <iostream>

class Color
{
public:
    virtual void accept(class Visitor*) = 0;
};

class Red: public Color
{
public:
    /*virtual*/
    void accept(Visitor*);
    void eye()
    {
        std::cout << "Red::eye\n";
    }
};
class Blu: public Color
{
public:
    /*virtual*/
    void accept(Visitor*);
    void sky()
    {
        std::cout << "Blu::sky\n";
    }
};

class Visitor
{
public:
    virtual void visit(Red*) = 0;
    virtual void visit(Blu*) = 0;
};

class CountVisitor: public Visitor
{
public:
    CountVisitor()
    {
        m_num_red = m_num_blu = 0;
    }
    /*virtual*/
    void visit(Red*)
    {
        ++m_num_red;
    }
    /*virtual*/void visit(Blu*)
    {
        ++m_num_blu;
    }
    void report_num()
    {
        std::cout << "Reds " << m_num_red << ", Blus " << m_num_blu << '\n';
    }
private:
    int m_num_red, m_num_blu;
};

class TemplateVisitor: public Visitor
{
public:
    TemplateVisitor() : num_of_colours(0) {}

    /*virtual*/
    template<class C>
    void visit(C* c)
    {
        ++num_of_colours;
    }
    void report_num()
    {
        std::cout << "Colours " << num_of_colours << '\n';
    }

private:
    int num_of_colours;

};


void Red::accept(Visitor *v)
{
    v->visit(this);
}

void Blu::accept(Visitor *v)
{
    v->visit(this);
}

int main()
{
    Color *set[] =
    {
        new Red, new Blu, new Blu, new Red, new Red, nullptr
    };
    CountVisitor count_operation;
    TemplateVisitor template_visitor;
    for (int i = 0; set[i]; i++)
    {
        set[i]->accept(&count_operation);
        set[i]->accept(&template_visitor);
    }
    count_operation.report_num();
    template_visitor.report_num();
}

Unfortunately, virtual methods and template methods can't match. 不幸的是,虚拟方法和模板方法无法匹配。

I mean... if your base class Visitor require 我的意思是...如果您的基层Visitor需要

virtual void visit(Red*) = 0;
virtual void visit(Blu*) = 0;

the implementation of two virtual methods in derived classes, you can't solve this obligation with a single template method 派生类中两个虚拟方法的实现,您不能使用单个模板方法解决此义务

template<class C>
void visit(C* c)
{
    ++num_of_colours;
}

You have to write two methods, absolutely not template, with the exact signature. 您必须编写两种具有完全签名的方法,绝对不是模板。 Maybe adding also override , to reduce the risk of mistakes. 也许还添加了override ,以减少出错的风险。

  void visit (Red * r) override
   { ++num_of_colours; }

  void visit (Blu * b) override
   { ++num_of_colours; }

Obviously you can define a template method (maybe with another name, but also visit() if you want) that is called by both virtual overrided methods 显然,您可以定义两个虚拟重写方法都调用的模板方法(可能使用其他名称,也可以根据需要visit() )。

  template <typename C>
  void visit (C * c)
   { ++num_of_colours; }

  void visit (Red * r) override
   { visit<Red>(r); }

  void visit (Blu * b) override
   { visit<Blu>(b); }

This way, you can implement the logic of the visitor in a single template method and call it by all virtual methods 这样,您可以在单个模板方法中实现访问者的逻辑,并通过所有虚拟方法进行调用

Why not just use a map and add some function to color to use as an identifier? 为什么不只使用地图并为颜色添加一些功能以用作标识符?

class Color
{    
public:
    virtual void accept(class Visitor*) = 0;
    virtual std::string color_name() = 0; 

};

class Visitor
{
public:
    virtual void visit(Color* c);
};

class CountVisitor: public Visitor
{
    std::unordered_map<std::string, int> map; 

public:
    /*virtual*/
    void visit(Color* c)
    {
        map[c.color_name()]++;
    }
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM