I've been reading about combinators for three days now and I finally started writing them in code (more like copying stuff from places and making sense of things).
Here's some code that I'm trying to run:
#include <iostream>
#include <utility>
template <typename Lambda>
class y_combinator {
private:
Lambda lambda;
public:
template <typename T>
constexpr explicit y_combinator (T&& lambda)
: lambda (std::forward <T> (lambda))
{ }
template <typename...Args>
decltype(auto) operator () (Args&&... args) {
return lambda((decltype(*this)&)(*this), std::forward <Args> (args)...);
}
};
template <typename Lambda>
decltype(auto) y_combine (Lambda&& lambda) {
return y_combinator <std::decay_t <Lambda>> (std::forward <Lambda> (lambda));
}
int main () {
auto factorial = y_combine([&] (auto self, int64_t n) {
return n == 1 ? (int64_t)1 : n * self(n - 1);
});
int n;
std::cin >> n;
std::cout << factorial(n) << '\n';
}
If I explicitly state the return type of the lambda as -> int64_t, everything works well. However, when I remove it, the compiler complains. The error:
main.cpp|16|error: use of 'main()::<lambda(auto:11, int64_t)> [with auto:11 = y_combinator<main()::<lambda(auto:11, int64_t)> >; int64_t = long long int]' before deduction of 'auto'
Why can't the compiler figure out the return type and deduce auto? I first thought that maybe I needed to change ... ? 1 : n * self(n - 1) to ... ? int64_t(1) : n * self(n - 1) so that the type of both return values ends up as int64_t and no possible ambiguities remain. This doesn't seem to be the case though. What am I missing?
Also, in the y_combinator class, declaring lambda as an object of type Lambda&& seems to cause problems. Why is this the case? This only happens when I write the cast in the operator () overload as (decltype(*this)&) instead of std::ref(*this). Are they doing different things?