c++ 具有模板化访问者的访问者模式

cetgtptt  于 2022-12-05  发布在  其他
关注(0)|答案(2)|浏览(127)

这是对使用标准变量和访问者模式时无用户定义转换的后续操作
我需要实现一个visitor pattern模板版本,如下所示,但是看起来accept函数必须是虚拟的,这是不可能的。你能帮助我吗?

#include <variant>
#include <iostream>

class Visitable //I need this to be non-templated (no template for Visitable!!): Otherwise I could use CRTP to solve this issue.
{
public:
    virtual ~Visitable() = default;

    template<typename Visitor> 
    /*virtual*/ double accept(Visitor* visitor) //I can't do virtual here.
    {
        throw("I don't want to end up here");
    };

protected:
    Visitable() = default;
};

struct DoubleVisitable : public Visitable
{
    template<typename Visitor> 
    double accept(Visitor* visitor) 
    {
        return visitor->visit(*this);
    };

    double m_val = 1.0;
};

struct StringVisitable : public Visitable
{
    template<typename Visitor> 
    double accept(Visitor* visitor) 
    {
        return visitor->visit(*this);
    };
    double m_val = 0.0;
};

template<typename... args>
class Visitor
{
public:
    virtual ~Visitor() = default;

    virtual double visit(typename std::variant<args...> visitable)
    {
        auto op = [this](typename std::variant<args...> visitable) -> double { return this->apply(visitable); };
        return std::visit(std::ref(op), visitable);
    }

    virtual double apply(typename std::variant<args...> visitable) = 0;

    Visitor() = default;
};

class SubVisitor : public Visitor<DoubleVisitable, StringVisitable>
{
public:
    virtual ~SubVisitor() = default;
    SubVisitor() : Visitor<DoubleVisitable, StringVisitable>() {};
    
    virtual double apply(std::variant<DoubleVisitable, StringVisitable> visitable) override
    {
        return std::visit(            
            [this](auto&& v){return process(v);},
            visitable
        );
    };

    virtual double process(const StringVisitable& visitable)
    {
        std::cout << "STRING HANDLED" << std::endl;
        return 0.0;
    }

    virtual double process(const DoubleVisitable& visitable)
    {
        std::cout << "DOUBLE HANDLED" << std::endl;
        return 1.0;
    }
};


int main(int argc, char* argv[])
{
    SubVisitor visitor;
    DoubleVisitable visitable;
    visitable.accept(&visitor);

    //I want to be doing this:
    Visitable* doubleV = new DoubleVisitable();
    doubleV->accept(&visitor);
    delete doubleV;
    return 1;
}

代码在这里Link。你能不能帮我使这个不抛出,但折叠到右边的子类DoubleVisitableStringVisitable。它看起来像我需要虚拟模板成员函数,这是不可能的,因为这里提到Can a class member function template be virtual?

gwbalxhn

gwbalxhn1#

在C++中,没有templatevirtual函数。这是不存在的。您可以做的是:

  • 对于您要访问的每个类(每个后代),都有一个accept方法
  • 具有std::variant<>实现而不是继承。
vsikbqxv

vsikbqxv2#

问题中说Visitable不能是模板。但是允许它从模板类 * 继承 * 吗?你知道所有可能的访问者吗?如果是,你可以添加一个新的模板类,Visitable从它继承,并为所有访问者声明虚方法:

template <typename ... T> class AcceptMethods {};
template <> class AcceptMethods<> {};
template <typename First, typename ... Rest>
class AcceptMethods<First, Rest...> : public AcceptMethods<Rest...> {
public:
  virtual double accept(First* ) = 0;
  virtual ~AcceptMethods() {}
};

typedef AcceptMethods<SubVisitor> AllAcceptMethods;

class Visitable : public AllAcceptMethods
{
public:
    virtual ~Visitable() = default;
};

在上面的代码中,我们只列出了SubVisitor,但是AcceptMethods是可变的,所以它可能是typedef AcceptMethods<A, B, C, D, AndSoOn> AllAcceptMethods;
然后,我们添加另一个模板类WithGenericAcceptMethod,其目的是通过调用模板方法acceptT来实现由AcceptMethods声明的accept方法:

template <typename This, typename ... T> class WithGenericAcceptMethod {};
template <typename This> class WithGenericAcceptMethod<This, AcceptMethods<>> : public Visitable {};
template <typename This, typename First, typename ... Rest>
class WithGenericAcceptMethod<This, AcceptMethods<First, Rest...>> : public WithGenericAcceptMethod<This, AcceptMethods<Rest...>> {
public:
  double accept(First* visitor) override {
    return ((This*)this)->template acceptT<First>(visitor);
  }
  virtual ~WithGenericAcceptMethod() {}
};

这个类的第一个参数是一个This参数,这符合CRTP的精神。然后我们可以让特定的可访问类继承WithGenericAcceptMethod,并实现模板acceptT方法:

struct DoubleVisitable : public WithGenericAcceptMethod<DoubleVisitable, AllAcceptMethods>
{
    template<typename Visitor> 
    double acceptT(Visitor* visitor) 
    {
        return visitor->visit(*this);
    };

    double m_val = 1.0;
};

struct StringVisitable : public WithGenericAcceptMethod<StringVisitable, AllAcceptMethods>
{
    template<typename Visitor> 
    double acceptT(Visitor* visitor) 
    {
        return visitor->visit(*this);
    };
    double m_val = 0.0;
};

相关问题