C++ Implementation of a Python-like memoization decorator Coming from a Python background, one thing I really miss in C++ is a memoization decorator (like functools.lru_cache. As I sometimes compete on Codeforces, I found myself implementing a similar thing in C++17 in case I ever need a quick and easy way to memoize function calls. I was wondering whether I could get some feedback on my implementation, and whether something like this could be effective and practical to use in production code.
Thanks!
#include &lt;bits/stdc++.h&gt;

// Using a map
template &lt;typename RetType, typename... ArgTypes&gt;
auto memoize(std::function&lt;RetType(ArgTypes...)&gt; func) -&gt; decltype(func) {
    return [
        memo = std::map&lt;std::tuple&lt;ArgTypes...&gt;, RetType&gt;(), 
        func = std::move(func)
    ](ArgTypes... args) mutable -&gt; RetType {
        auto key = make_tuple(args...);
        if (auto it = memo.find(key); it != memo.end()) {
            return it-&gt;second;
        }
        auto [it, emplaced] = memo.emplace(key, func(args...));
        return it-&gt;second;
    };
}


// Using an unordered_map

// From boost
template&lt;typename T&gt;
inline void hash_combine(std::size_t&amp; seed, const T&amp; val) {
    std::hash&lt;T&gt; hasher;
    seed ^= hasher(val) + 0x9e3779b9 + (seed &lt;&lt; 6) + (seed &gt;&gt; 2);
}
template &lt;typename RetType, typename... ArgTypes&gt;
auto memoizeHashed(std::function&lt;RetType(ArgTypes...)&gt; func) -&gt; decltype(func) {
    using KeyType = std::tuple&lt;ArgTypes...&gt;;
    struct KeyHashFnc {
        size_t operator() (const KeyType&amp; key) const {
            size_t result = 0;
            std::apply([&amp;result](auto&amp;&amp;... args) {
                (
                    hash_combine(result, std::forward&lt;decltype(args)&gt;(args)), 
                    ...
                );
            }, key);
            return result;
        }
    };
    return [
        memo = std::unordered_map&lt;KeyType, RetType, KeyHashFnc&gt;(), 
        func = std::move(func)
    ](ArgTypes... args) mutable -&gt; RetType {
        auto key = std::make_tuple(args...);
        if (auto it = memo.find(key); it != memo.end()) {
            return it-&gt;second;
        }
        auto [it, emplaced] = memo.emplace(key, func(args...));
        return it-&gt;second;
    };
}


// Intended usage (solves the problem: https://leetcode.com/problems/minimum-difficulty-of-a-job-schedule/)
using namespace std;
int INF = 1e9+5;
class Solution {
public:
    int minDifficulty(const vector&lt;int&gt;&amp; arr, int d) {
        int N = arr.size();

        using DpFuncType = function&lt;int(int,int)&gt;;
        DpFuncType dp = [&amp;dp, &amp;arr, N](int i, int k) {
            int n = N-i;

            if (k &lt;= 0) {
                return -1;
            }
            else if (k &gt; n) {
                return -1;
            }
            else if (k == n) {
                return accumulate(arr.begin()+i, arr.end(), 0);
            }
            else { // k &lt; n
                if (k == 1) {
                    return *max_element(arr.begin()+i, arr.end());
                }
                else {
                    int result = INF;
                    int maxVal = arr[i];
                    for (int j = i+1; j &lt; N; ++j) {
                        int curr = dp(j,k-1);
                        if (curr != -1) {
                            result = min(result, maxVal + curr);
                        }
                        maxVal = max(maxVal, arr[j]);
                    }

                    if (result == INF) {
                        return -1;
                    }
                    else {
                        return result;
                    }
                }
            }            
        };
        dp = memoizeHashed(dp); // memoize(...) can also be used here

        return dp(0,d);
    }
};
```
Only #include public headers from the STL
You should not #include &lt;bits/stdc++.h&gt;. It is a non-standard header that is not guaranteed to exist or work as you expect it to. If you do this regularly, please unlearn this, and instead #include the proper header files. You only need two for your memoizer:
#include &lt;map&gt;
#include &lt;functional&gt;

No need for trailing return types
I see you specify trailing return types for several of the functions and lambdas you define, but I do not think they are not necessary here. The compiler can automatically deduce them, and while returning a lambda with an auto return type is not the same as when the return type is explicitly set to a std::function as mentioned by HTNW, the caller can always do this conversion themselves if necessary, and indeed that is what happens when you write dp = memoize(...) inside minDifficulty().
Consider rewriting it as a class
Instead of writing memoize() as a function that returns a mutable lambda function, I would just go for the less exciting way of writing a class. The main reason is that more people will be familiar with it, and if you ever want to add functionality to the memoizer, a class makes that much easier. Here is how it could look:
template &lt;typename RetType, typename... ArgTypes&gt;
class memoize {
    std::function&lt;RetType(ArgTypes...)&gt; func;
    std::map&lt;std::tuple&lt;ArgTypes...&gt;, RetType&gt; memo;
public:
    memoize(std::function&lt;RetType(ArgTypes...)&gt; func): func(func) {}
    auto operator()(ArgTypes... args) {
        auto key = std::make_tuple(args...);
        if (auto it = memo.find(key); it != memo.end()) {
            return it-&gt;second;
        }
        auto [it, emplaced] = memo.emplace(key, func(args...));
        return it-&gt;second;
    };
};

This is actually a complete drop-in replacement for the lambda, even this line will still work:
dp = memoize(dp);

Use in production code
Your memoizer could be used in production code, sure! However, apart from coding challenges I've found the number of cases where I would ever need this to be vanishingly small. Either things need to be remembered much longer than the lifetime of a program, in which case a proper database is used, or all the values you could probably want are precomputed once and included with the binary, or the algorithm I am implementing would be just as easy to implement by reading/writing to a container directly instead of using a memoizer function.
A std::map or std::unordered_map is also not always the most efficient way to store things, often a std::vector is much more efficient if the function just gets a single integer argument. And your memoizer also doesn't handle the case where some parameters to the function are not part of the key to be memoized, although you could work around that by using std::bind() or some other form of currying.
In short, there's nothing wrong with it as far as I can see, but I think there won't be very many occasions where you would need it.