P0792R14: function_ref
: a type-erased callable reference
P0792R14 is a proposal for C++26 that introduces std::function_ref
, a vocabulary type with reference semantics for passing callable objects.
Motivation
The higher-order function pattern is common in C++, such as the comparator of std::ranges::sort
or the predicate of std:::views::filter
. There are multiple methods to accept a higher-order function as a function parameter:
- Function pointer: This approach has the minimum overhead, but it doesn't support function objects.
- Template parameter: This approach supports all callable objects, but it requires the implementation to constraint the signature (with
std:is_invocable_v
, for example) and convert the function to a function template. std::function
orstd::move_only_function
: This approach supports all callable objects, but it requires them to be copy-constructible or move-constructible, respectively. The call wrappers start to allocate the callable objects when they can't fit in a small buffer.
The proposed function_ref
supports all callable objects regardless of whether they are copy-constructible or move-constructible. It has minimum overhead, as it avoids allocations and exceptions. Moreover, certain calling conventions can pass its instances in registers.
Design
template <typename R, typename... ArgTypes> class function_ref;
template <typename R, typename... ArgTypes>
class function_ref<R(ArgTypes...) FUNCTION_REF_CV noexcept(FUNCTION_REF_NOEXCEPT)>;
The <functional>
header provides a partial specialization of function_ref
for each combination of the qualifiers on the function type:
FUNCTION_REF_CV
is either empty orconst
.FUNCTION_REF_NOEXCEPT
is eithertrue
orfalse
.
Each specialization of function_ref
is a trivially copyable type that models copyable
. Because function_ref
has the reference semantic, its operator()
is const
-qualified and it enforces the callable object to be lvalue-callable.
std::function_ref
can be constructed from a function or a function object. However, it cannot be constructed from a pointer to a member function or a pointer to a data member. This limitation exists because pointers to member functions have a different representation than regular function pointers, requiring at least twice the space in Clang. Including support for member function pointers would increase the size of std::function_ref
.
template <typename R, typename... ArgTypes> class function_ref {
template <class F> function_ref(F *f) noexcept;
template <class F> constexpr function_ref(F &&f) noexcept;
};
Since the representation of pointers to member functions or data members is known at compile time, they can be accepted as non-type template parameters, meaning they won't occupy any storage. To support this, std::function_ref
provides constructors that accept a specialization of std::nontype_t
, which wraps a non-type template parameter, along with an optional object to which the member pointer will be bound, effectively converting it into a regular function.
template <typename R, typename... ArgTypes> class function_ref {
template <auto f> constexpr function_ref(nontype_t<f>) noexcept;
template <auto f, class U>
constexpr function_ref(nontype_t<f>, U &&obj) noexcept;
template <auto f, class T>
constexpr function_ref(nontype_t<f>, FUNCTION_REF_CV T *obj) noexcept;
};