The proper way to do memoization in C ++ is to mix Y-combinator in.
Your basic function needs to be modified. Instead of calling itself directly, it takes a template reference to itself as its first argument (or recursion std::function<Same_Signature> as its first argument).
Let's start with the Y-combinator . Then add the cache to operator() and rename it to memoizer and give it a fixed signature (for the table).
It remains only to write tuple_hash<template<class...>class Hash> , which makes a hash on the tuple.
The type of function that can be memoized, (((Args...)->R), Args...) -> R , which makes a memoizer of type ( (((Args...) -> R), Args...) -> R ) -> ((Args...) -> R) . It is also useful to use Y-combinator to create a "traditional" recursive implementation.
Note that if the memoized function changes its arguments during a call, memoizer caches the results in the wrong place.
struct wrap {}; template<class Sig, class F, template<class...>class Hash=std::hash> struct memoizer; template<class R, class...Args, class F, template<class...>class Hash> struct memoizer<R(Args...), F, Hash> { using base_type = F; private: F base; std::unordered_map< std::tuple<std::decay_t<Args>...>, R, tuple_hash<Hash> > cache; public: template<class... Ts> R operator()(Ts&&... ts) const { auto args = std::make_tuple(ts...); auto it = cache.find( args ); if (it != cache.end()) return it->second; auto&& retval = base(*this, std::forward<Ts>(ts)...); cache.emplace( std::move(args), retval ); return decltype(retval)(retval); } template<class... Ts> R operator()(Ts&&... ts) { auto args = std::tie(ts...); auto it = cache.find( args ); if (it != cache.end()) return it->second; auto&& retval = base(*this, std::forward<Ts>(ts)...); cache.emplace( std::move(args), retval ); return decltype(retval)(retval); } memoizer(memoizer const&)=default; memoizer(memoizer&&)=default; memoizer& operator=(memoizer const&)=default; memoizer& operator=(memoizer&&)=default; memoizer() = delete; template<typename L> memoizer( wrap, L&& f ): base( std::forward<L>(f) ) {} }; template<class Sig, class F> memoizer<Sig, std::decay_t<F>> memoize( F&& f ) { return {wrap{}, std::forward<F>(f)}; }
live example with a hard-coded hash function based on this SO post .
auto fib = memoize<size_t(size_t)>( [](auto&& fib, size_t i)->size_t{ if (i<=1) return 1; return fib(i-1)+fib(i-2); } );