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?
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.