Search code examples
c++templatesvirtual-functionsvisitor-pattern

C++ Visitor Pattern with variable return types


I have the following setup:

class IVisitor{
public:
    virtual SomeType visit(IVisitable& visitable) = 0;
};

class IVisitable{
public:
    virtual SomeType accept(IVisitor& visitor) = 0;
};

class ConcreteVisitor : public IVisitor{
public:
    SomeType visit(IVisitable& visitable) override {
        return calculateSomeStuff();
    }
};

class ConcertVisitable : public IVisitable{
public:
    SomeType accept(IVisitor& visitor) override {
        return visitor.visit(*this);
    }
};

But I want different Visitors to return different types. For this to work, IVisitable::accept() should also return different types.

This would result in the following (incorrect) code:

template <typename R>
class IVisitor{
public:
    virtual R visit(IVisitable& visitable) = 0;
};

class IVisitable{
public:
    template <typename R>
    virtual R accept(IVisitor<R>& visitor) = 0;
};

class ConcreteVisitorOne : public IVisitor<int>{
public:
    int visit(IVisitable& visitable) override {
        return calculateSomeStuff();
    }
};

class ConcreteVisitorTwo : public IVisitor<bool>{
public:
    bool visit(IVisitable& visitable) override {
        return calculateSomeStuff();
    }
};

class ConcertVisitable : public IVisitable{
public:
    int accept<int>(IVisitor<int>& visitor) override {
        return visitor.visit(*this);
    }
};

This, unfortunately, is not possible in C++, as the virtual function IVistitable::accept() can't be templated.

Does anybody know a way around this limitation?


Solution

  • Several options:

    • Don't return anything, handle the value inside the visitor. The visitor is completely free to store or handle any return value and type.

    • Return a variadic type (std::any or std::variant). Note that using them requires you to find out the actual type, which is kind-of similar to your original problem perhaps.

    • Inherit multiple IVisitor<> interfaces. This might be the easiest way even, although it's not necessary elegant.