关于必须是虚拟的模板化函数的变通方法的问题在这里很常见,尽管我找不到任何有助于解决我的问题的方法,这是这个问题的一个简单变体:需要一个虚拟模板成员的变通方法
所提出的方法是使用类型擦除,从而获得非常干净和简单的解决方案。但是,如果我需要从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等)。
您基本上有两个选择:
-
为
accept()
提供一个常见的返回类型,如std::any
或其前身boost::any
。或者,如果您有一些有限数量的可能返回类型,std::variant
/boost::variant
。这样,成员函数就不需要是一个模板,但调用者必须知道如何处理它 -
将键入的结果存储在
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.; };