Concept Subspace :: sus :: fn :: FnOnce
template <class F, class... S>concept FnOnce
requires {
// Receives and passes along the signature as a pack instead of a single
// argument in order to consistently provide a static_assert() in `Sig` when
// `S` is not a function signature.
{
__private::InvokedFnOnce<F, typename __private::Sig<S...>::Args>::returns()
} -> __private::ValidReturnType<typename __private::Sig<S...>::Return>;
}
The version of a callable object that may be called only once.
A FnOnce
is typically the best fit for any callable that will
only be called at most once.
A type that satisfies FnOnce
will return a type that can be converted to
R
when called with the arguments Args...
. FnOnce
is satisfied by
being callable as an rvalue (which is done by providing an operator() that
is not &
-qualified). Mutable and const lambdas will satisfy FnOnce
.
The second argument of FnOnce<F, S>
is a function signature with the
format ReturnType(Args...)
, where Args...
are the arguments that will
be passed to the FnOnce
and ReturnType
is what is expected to be
received back. It would appear as a matching concept as:
void function(FnOnce<ReturnType(Args...)> auto f) { ... }
Use of FnOnce
FnOnce
should be received by value typically. If received as a rvalue
(universal) reference, it should be constrained by
IsMoveRef<decltype(f)>
to avoid moving out of
an lvalue in the caller.
A FnOnce
should be called by moving it with sus::move()
when passing it
to call_once
along with any arguments.
This ensures the
correct overload is called on the object and that method pointers are
called correctly. It is moved-from after calling, and it should only be
called once.
Calling a FnOnce
multiple times may panic
or cause Undefined Behaviour.
Not moving the FnOnce
when calling it may fail to compile,
panic
, or cause Undefined Behaviour depending on the type
that is being used to satisfy FnOnce
.
Type erasure
Using a concept like FnOnce
in a function parameter requires the function
to be a template. Template functions can not be virtual, they must appear
in the header, and they can have a negative impact on binary size. So it can
be desirable to work with a FnOnce
without templates.
To do so, FnOnce
supports being type-erased, on the heap or the stack,
into a DynFnOnce
type.
To receive ownership of a type-erased FnOnce
, receive a
Box
<
DynFnOnce<R(Args...)>
>
instead.
To receive a reference to a type-erased FnOnce
, receive a
DynFnOnce<R(Args...)>&&
instead.
See DynConcept
for more on type erasure of
concept-satisfying types.
Compatibility
Any callable type that satisfies Fn
or FnMut
will also satisfy FnOnce
.
While a FnOnce
should only be called once, this is compatible with the
requirements of FnMut
and Fn
which can be called only a single time. As
well, FnOnce
is allowed to mutate internal state, but it does not have to,
which is compatible with the const nature of Fn
.
Examples
A function that receives a FnOnce
matching type and calls it:
// Accepts any type that can be called once with (Option<i32>) and returns
// i32.
i32 do_stuff_once(sus::fn::FnOnce<i32(sus::Option<i32>)> auto f) {
return sus::fn::call_once(sus::move(f), sus::some(400));
}
i32 x = do_stuff_once([](sus::Option<i32> o) -> i32 {
return sus::move(o).unwrap_or_default() + 4;
});
sus_check(x == 400 + 4);
A FnOnce
whose first parameter is a class can be matched with a method
from that same class if the remaining parameters match the method's
signature:
struct Class {
Class(i32 value) : value_(value) {}
i32 value() const { return value_; }
private:
i32 value_;
};
i32 map_class_once(const Class& c,
sus::fn::FnOnce<i32(const Class&)> auto f) {
return sus::fn::call_once(sus::move(f), c);
}
// Map the class C to its value().
auto c = Class(42);
sus_check(map_class_once(c, &Class::value) == 42);
Using a method pointer as the parameter for Option::map()
will call that
method on the object inside the Option:
struct Class {
Class(i32 value) : value_(value) {}
i32 value() const { return value_; }
private:
i32 value_;
};
auto o = sus::Option<Class>(Class(42));
sus_check(o.map(&Class::value) == sus::some(42));