具有模板化返回值解决方法的虚拟模板函数



关于必须是虚拟的模板化函数的变通方法的问题在这里很常见,尽管我找不到任何有助于解决我的问题的方法,这是这个问题的一个简单变体:需要一个虚拟模板成员的变通方法

所提出的方法是使用类型擦除,从而获得非常干净和简单的解决方案。但是,如果我需要从visit()方法返回一个值,该怎么办?OP在他的问题中已经有了这方面的内容,但由于他从未使用过结果,因此在解决方案中被忽略了。

现在想象一下这个代码:

template <typename T>
class BaseVisitor {
  public:
    BaseVisitor();
    T visit(BaseVisited *visited);
    virtual ~BaseVisitor();
}

class BaseVisited {
  BaseVisited();
  template <typename T>
    virtual T accept(BaseVisitor<T> *visitor) { return visitor->visit(this); };
  virtual ~BaseVisited();
}

即使在应用了类型擦除技巧之后,我们仍然需要对accept()进行模板化。还有什么想法吗?

注意:我不能像SO上的一些答案中所建议的那样,使用基类作为返回值,因为T也可以代表任何基类型(int、string等)。

您基本上有两个选择:

  1. accept()提供一个常见的返回类型,如std::any或其前身boost::any。或者,如果您有一些有限数量的可能返回类型,std::variant/boost::variant。这样,成员函数就不需要是一个模板,但调用者必须知道如何处理它

  2. 将键入的结果存储在Visitor对象中。您必须存储它,而不是返回它,但至少您可以保留类型。您可以使用访问者模式来处理此问题。我们可以有不同的访问者,他们有不同的结果类型;他们只是将它们存储在内部:

    // a void visitor
    struct ShapePrinter : IShapeVisitor
    {
        void visit(const Square&) override { std::cout << "Square"; }
        void visit(const Circle&) override { std::cout << "Circle"; }
    };
    // a double visitor
    struct ShapePerimeterComputer : IShapeVisitor
    {
        void visit(const Square& square) override { perimeter = 4. * square.sideLength; }
        void visit(const Circle& circle) override { perimeter = 2. * M_PI * circle.radius; }
        double perimeter = 0.;
    };
    

最新更新