Defined in header <memoized_invoke.hh>
class single_threaded;
single_threaded is the default ExecutionPolicy for memoized_invoke — the one CTAD selects. It tracks the run state in a plain, non-atomic enum member (not_started / running / done). Every operation is a single load or store; there is no overhead over a hand-written bool ran_ flag.
It is not thread-safe. Two threads calling operator() on the same memoized_invoke can both observe not_started and both run the callable. Use it when the object is confined to one thread, when an external lock already serialises access, or when you only want the return-value cache and reset() and the one-time guarantee comes from elsewhere (a function-local static, say). For a first-run race across threads, use lock_free.
Member functions
| Function | Effect |
|---|---|
bool try_enter() | If the state is done, returns false. Otherwise sets it to running and returns true. |
void mark_done() noexcept | Sets the state to done. |
void mark_free() noexcept | Sets the state to not_started (used by reset() and by exception rollback). |
bool is_done() const noexcept | true when the state is done. |
void wait() const noexcept | No-op — a single-threaded caller is never actually blocked. |
Special members
Default-, copy- and move-constructible and copy- and move-assignable, all implicitly defined. Copy and move copy the state like any enum member — so a copy of a memoized_invoke<single_threaded, …> that is done is itself done and serves the cached value without re-running. (This is the opposite of lock_free, whose copy/move reset the state.)
<a id="the-executionpolicy-interface"></a>The ExecutionPolicy interface
memoized_invoke uses its policy only through these five members. Any type that provides them — with these semantics — can be the policy:
| Expression | Returns | Semantics |
|---|---|---|
p.try_enter() | bool | Atomically move not-started → running. true iff this call won the race. |
p.mark_done() | void | Move to done. Wake any waiters. |
p.mark_free() | void | Move to not-started (reset / rollback). Wake any waiters. |
p.is_done() | bool | true while in the done state. |
p.wait() | void | Block until the state is no longer running, then return (the caller re-checks). |
The type must be default-, copy- and move-constructible and copy- and move-assignable. Copy and move may re-initialise the state. lock_free is the reference implementation of a synchronising policy; a std::mutex + std::condition_variable version is a short exercise (see User Guide › Execution Policies).
Example
#include <memoized_invoke.hh>
#include <cassert>
using fedem::utility::memoized_invoke;
using fedem::utility::single_threaded;
int main()
{
memoized_invoke co( []{ return 7; } ); // single_threaded, deduced by CTAD
assert( co() == 7 );
assert( co.is_done() );
auto copy = co; // state copied — copy is already done
assert( copy.is_done() );
assert( copy() == 7 ); // no re-run
}
See also
- lock_free — the thread-safe policy.
- make_memoized — how to choose a non-default policy.
- User Guide › Execution Policies.

