The reference manual describes the callonce public API in cppreference-style one-page-per-symbol format. The public surface is small, so this is a short manual: one class template with four member pages, the two execution policies, the factory, and the deduction guides.
The generated Doxygen tree is the exhaustive machine-produced reference — every signature, the class and collaboration diagrams, the include graph, the call/caller graphs. This manual is the human-written companion: same symbols, the why rather than only the what.
The library
Everything is in namespace fedem::utility, declared in one header:
#include <memoized_invoke.hh>
No dependencies beyond <atomic>, <functional>, <optional>, <tuple>, <type_traits> and <utility>.
Symbols
| Symbol | Kind | One-line summary |
|---|---|---|
memoized_invoke | class template | Owns a callable + argument set; runs it at most once; caches the result. |
single_threaded | class | Zero-overhead execution policy. Not thread-safe. The CTAD default. |
lock_free | class | Thread-safe execution policy — atomic CAS + C++20 atomic::wait. |
make_memoized | function template | Build a memoized_invoke with an explicit policy, deducing F and Args. |
| deduction guides | — | Let CTAD deduce every template parameter (always single_threaded). |
memoized_invoke members
| Member | Page |
|---|---|
| (constructors), copy, move, assignment | memoizedinvoke::memoizedinvoke |
operator() — both overloads | memoized_invoke::operator() |
reset — both overloads | memoized_invoke::reset |
is_done, value | memoizedinvoke::isdone, ::value |
result_type (member type) | memoized_invoke |
For new readers
If you are starting from zero, read User Guide › Getting Started first — it walks the whole model in one page. Then come back here for the per-symbol detail, and see User Guide › Execution Policies for the single_threaded / lock_free decision.
Design decisions worth knowing about
Args... are explicit template parameters. The type is memoized_invoke<ExecutionPolicy, F, Args...>, not something that deduces argument types from F alone. That is what lets it accept std::bind_front results (templated operator(), no fixed signature), std::function, and member function pointers uniformly, with std::invoke_result_t<F, Args...> doing all the work.
Copy/move reset the lock_free state. std::atomic is not copyable, and a copy owns a separate synchronisation context, so lock_free's copy and move re-initialise the state to not-started. The cached value is still copied. single_threaded copies its state like any member. This surfaces on the constructor and lock_free pages.
One argument slot, no map. operator()(args...) compares against a single stored argument tuple. Switching arguments and switching back is two cache misses. A multi-key memoization table is a deliberate non-goal.

