From here on a (endo)functor is something able to take an object and transform it in another object of the same type. The simplest example of functor is the identity:
struct Identity {
template <typename T>
T Apply(T x) {
return x
}
};
I need to have a "Functor type" that identifies a generic Functor. What I would like to do is write code like:
class Sum {
public:
Sum(Functor* f, Functor* g) :
f_(f),
g_(g) {}
template <typename T>
T Apply(T x) { return f_->Apply(x) + g_->Apply(x); }
private
Functor* f_;
Functor* g_;
};
The first idea that came to my mind is of course using a virtual class:
struct Functor {
template <typename T>
virtual T Apply(T x) = 0;
};
The unsolvable problem with this approach is that templates cannot be virtual.
Then I tried using C++ concepts. But, as stated in Specifying a concept for a type that has a member function template using Concepts Lite and C++ Concepts: Can I define a concept that is itself a template? it is not possible to have a "templated concept".
Finally I have stumbled upon How to achieve "virtual template function" in C++ and therefore I came up with the following possible implementation:
struct Functor {
template <typename T>
T Apply(const T& x); // No more virtual!!!
};
// ... Identity and Sum declarations properly inheriting from Functor ...
template <typename T>
T Functor::Apply(T x) {
if (Identity* specialized =
dynamic_cast<Identity*>(this)) {
return specialized->Apply(x);
} else if (const Sum* specialized =
dynamic_cast<const Sum*>(this)) {
return specialized->Apply(x);
} else ...
}
Even though this is compiling, it's not the best solution. The main issues are: performance and code repetition. The performance issue comes from the fact that each time Apply is called on a Functor the long if clause inside Functor::Apply must be resolved. This is a big problem as Functor can be deeply nested (so calling Apply may result in multiple call to Functor::Apply). The "code repetition" issue is quite self evident as each time I want to define a new Functor I have also to modify Functor::Apply adding a new if clause.
What I am asking here is whether there is a proper (cleaner) way to define a Functor interface/concept that makes possible creating classes like Sum. C++ concepts and heavy template metaprogramming is accepted.
p.s. All the code snippets have been kept as simple as possible on purpose. Avoid suggesting to use class instead of struct or to add const identifiers or to use unique pointers, it's not the point of this question.