Defined in header <memoized_invoke.hh>
template< typename ExecutionPolicy, typename F, typename... Args >
requires std::is_invocable_v< F, Args... >
class memoized_invoke;
memoized_invoke wraps a callable F and a fixed argument set Args..., runs it at most once, and caches the return value. operator()() runs the callable on the first call and returns the cached value on every later call. operator()(new_args...) compares the new arguments with the stored ones and re-executes when they differ.
If the callable throws, an internal RAII guard rolls the execution state back to not-started, the cache stays empty, and the next call retries — the same contract as std::call_once.
Template parameters
| Parameter | Description |
|---|---|
ExecutionPolicy | single_threaded, lock_free, or any type meeting the ExecutionPolicy interface. Controls whether concurrent first calls are synchronised. |
F | The callable type. Any F for which std::is_invocable_v<F, Args...> holds — free / member function pointers (including noexcept), lambdas, functors, std::function, std::bind_front results. Moved into the object. |
Args... | The argument types F is invoked with. For a member function pointer, the object (pointer or reference) is the first entry. Stored by value as std::decay_t<Args>.... |
The requires std::is_invocable_v<F, Args...> clause is a hard constraint — an ill-formed F/Args combination is not a substitution failure you can recover from, it is a compile error at the class.
Member types
| Member type | Definition |
|---|---|
result_type | std::invoke_result_t<F, Args...>. The type operator() returns and value() yields a reference to. May be void, in which case operator() returns nothing and value() is not available. |
Member functions
| (constructor) | constructs the wrapper from a callable and, if any, its arguments |
| (destructor) | trivial — the object owns only value members |
| operator= | copy- and move-assignment (self-assignment safe) |
| operator() | runs the callable once / returns the cached result; the arg overload re-runs on change |
| reset | clears the cache; the arg overload also replaces the stored arguments |
| is_done | whether the callable has completed and a value is cached |
| value | const reference to the cached value, without invoking the callable |
Non-member helpers
| make_memoized | factory that picks a non-default ExecutionPolicy while deducing F / Args |
| deduction guides | enable CTAD; always select single_threaded |
Notes
The object holds, in order: the callable, the policy, a return_storage<result_type> (a std::optional, or an empty struct for void), and a std::tuple<std::decay_t<Args>...> of the arguments.
memoized_invoke has no noexcept default constructor and no constexpr support. It is a run-time facility.
Example
#include <memoized_invoke.hh>
#include <iostream>
#include <string>
using fedem::utility::memoized_invoke;
std::string build_greeting(std::string const& who)
{
std::cout << "[build] for " << who << '\n';
return "Hello, " + who + '!';
}
int main()
{
memoized_invoke greet( &build_greeting, std::string("Ada") );
std::cout << greet() << '\n'; // [build] for Ada / Hello, Ada!
std::cout << greet() << '\n'; // Hello, Ada! (cached)
std::cout << greet(std::string("Grace")) << '\n'; // [build] for Grace / Hello, Grace!
greet.reset();
std::cout << greet.is_done() << '\n'; // 0
std::cout << greet() << '\n'; // [build] for Grace / Hello, Grace!
}
See also
- make_memoized — build one with
lock_free. - single_threaded / lock_free — the policies.
- User Guide › Getting Started — the model in one page.

