std::bind_front, std::bind_back - cppreference.com (original) (raw)
Function templates std::bind_front
and std::bind_back
generate a perfect forwarding call wrapper which allows to invoke the callable target with its (1,2) first or (3,4) last sizeof...(Args) parameters bound to args.
1,3) The call wrapper holds a copy of the target callable object f.
2,4) The call wrapper does not hold a callable target (it is statically determined).
std::invoke(ConstFn, bound_args..., call_args...).
std::invoke(ConstFn, call_args..., bound_args...).
The following conditions must be true, otherwise the program is ill-formed:
- (1,3) std::is_constructible_v<std::decay_t<F>, F>,
- (1,3) std::is_move_constructible_v<std::decay_t<F>>,
- (2,4) If decltype(ConstFn) is a pointer or a pointer-to-member then
ConstFn
is not a null pointer, - (std::is_constructible_v<std::decay_t<Args>, Args> && ...),
- (std::is_move_constructible_v<std::decay_t<Args>> && ...).
Contents
[edit] Parameters
f | - | Callable object (function object, pointer to function, reference to function, pointer to member function, or pointer to data member) that will be bound to some arguments |
---|---|---|
args | - | list of the arguments to bind to the (1,2) first or (3,4) last sizeof...(Args) parameters of the callable target |
Type requirements | ||
-std::decay_t<F> must meet the requirements of MoveConstructible. | ||
-std::decay_t<Args>... must meet the requirements of MoveConstructible. | ||
-decltype(ConstFn) must meet the requirements of Callable. |
[edit] Return value
A function object (the call wrapper) of type T
that is unspecified, except that the types of objects returned by two calls to std::bind_front
or std::bind_back
with the same arguments are the same.
Let _bind-partial_
be either std::bind_front
or std::bind_back
.
The returned object has the following properties:
bind-partial return type
Member objects
The returned object behaves as if it holds:
1,3) A member object fd
of type std::decay_t<F> direct-non-list-initialized from std::forward<F>(f), and
1-4) An std::tuple object tup
constructed with std::tuple<std::decay_t<Args>...>(std::forward<Args>(args)...), except that the returned object's assignment behavior is unspecified and the names are for exposition only.
Constructors
The return type of _bind-partial_
behaves as if its copy/move constructors perform a memberwise copy/move. It is CopyConstructible if all of its member objects (specified above) are CopyConstructible, and is MoveConstructible otherwise.
Member function operator()
Given an object G
obtained from an earlier call to (1,3) _bind-partial_(f, args...)
or (2,4) _bind-partial_<ConstFn>(args...)
, when a glvalue g
designating G
is invoked in a function call expression g(call_args...), an invocation of the stored object takes place, as if by:
std::invoke(g.fd, std::get<Ns>(g.tup)..., call_args...), when
_bind-partial_
isstd::bind_front
,std::invoke(ConstFn, std::get<Ns>(g.tup)..., call_args...), when
_bind-partial_
isstd::bind_front
,std::invoke(g.fd, call_args..., std::get<Ns>(g.tup)...), when
_bind-partial_
isstd::bind_back
,std::invoke(ConstFn, call_args..., std::get<Ns>(g.tup)...), when
_bind-partial_
isstd::bind_back
,
where
Ns
is an integer pack0, 1, ..., (sizeof...(Args) - 1)
,g
is an lvalue in the std::invoke expression if it is an lvalue in the call expression, and is an rvalue otherwise. Thus std::move(g)(call_args...) can move the bound arguments into the call, where g(call_args...) would copy.
The program is ill-formed if g
has volatile-qualified type.
The member operator() is noexcept if the std::invoke expression it calls is noexcept (in other words, it preserves the exception specification of the underlying call operator).
[edit] Exceptions
1,3) Throw any exception thrown by calling the constructor of the stored function object.
1-4) Throw any exception thrown by calling the constructor of any of the bound arguments.
[edit] Notes
These function templates are intended to replace std::bind. Unlike std::bind
, they do not support arbitrary argument rearrangement and have no special treatment for nested bind-expressions or std::reference_wrappers. On the other hand, they pay attention to the value category of the call wrapper object and propagate exception specification of the underlying call operator.
As described in std::invoke, when invoking a pointer to non-static member function or pointer to non-static data member, the first argument has to be a reference or pointer (including, possibly, smart pointer such as std::shared_ptr and std::unique_ptr) to an object whose member will be accessed.
The arguments to std::bind_front
or std::bind_back
are copied or moved, and are never passed by reference unless wrapped in std::ref or std::cref.
Typically, binding arguments to a function or a member function using (1) std::bind_front
and (3) std::bind_back
requires storing a function pointer along with the arguments, even though the language knows precisely which function to call without a need to dereference the pointer. To guarantee "zero cost" in those cases, C++26 introduces the versions (2,4) (that accept the callable object as an argument for constant template parameter).
Feature-test macro | Value | Std | Feature |
---|---|---|---|
__cpp_lib_bind_front | 201907L | (C++20) | std::bind_front, (1) |
202306L | (C++26) | Allow passing callable objects as constant template arguments to std::bind_front, (2) | |
__cpp_lib_bind_back | 202202L | (C++23) | std::bind_back, (3) |
202306L | (C++26) | Allow passing callable objects as constant template arguments to std::bind_back, (4) |
[edit] Possible implementation
(2) bind_front |
---|
namespace detail { template<class T, class U> struct copy_const : std::conditional<std::is_const_v<T>, U const, U> {}; template<class T, class U, class X = typename copy_const<std::remove_reference_t<T>, U>::type> struct copy_value_category : std::conditional<std::is_lvalue_reference_v<T&&>, X&, X&&> {}; template <class T, class U> struct type_forward_like : copy_value_category<T, std::remove_reference_t<U>> {}; template <class T, class U> using type_forward_like_t = typename type_forward_like<T, U>::type; } template<auto ConstFn, class... Args> constexpr auto bind_front(Args&&... args) { using F = decltype(ConstFn); if constexpr (std::is_pointer_v<F> or std::is_member_pointer_v<F>) static_assert(ConstFn != nullptr); return [... bound_args(std::forward<Args>(args))]<class Self, class... T> ( this Self&&, T&&... call_args ) noexcept ( std::is_nothrow_invocable_v<F, detail::type_forward_like_t<Self, std::decay_t<Args>>..., T...> ) -> std::invoke_result_t<F, detail::type_forward_like_t<Self, std::decay_t<Args>>..., T...> { return std::invoke(ConstFn, std::forward_like<Self>(bound_args)..., std::forward<T>(call_args)...); }; } |
(4) bind_back |
namespace detail { /* is the same as above */ } template<auto ConstFn, class... Args> constexpr auto bind_back(Args&&... args) { using F = decltype(ConstFn); if constexpr (std::is_pointer_v<F> or std::is_member_pointer_v<F>) static_assert(ConstFn != nullptr); return [... bound_args(std::forward<Args>(args))]<class Self, class... T> ( this Self&&, T&&... call_args ) noexcept ( std::is_nothrow_invocable_v<F, detail::type_forward_like_t<Self, T..., std::decay_t<Args>>...> ) -> std::invoke_result_t<F, detail::type_forward_like_t<Self, T..., std::decay_t<Args>>...> { return std::invoke(ConstFn, std::forward<T>(call_args)..., std::forward_like<Self>(bound_args)...); }; } |
[edit] Example
#include #include int minus(int a, int b) { return a - b; } struct S { int val; int minus(int arg) const noexcept { return val - arg; } }; int main() { auto fifty_minus = std::bind_front(minus, 50); assert(fifty_minus(3) == 47); // equivalent to: minus(50, 3) == 47 auto member_minus = std::bind_front(&S::minus, S{50}); assert(member_minus(3) == 47); //: S tmp{50}; tmp.minus(3) == 47 // Noexcept-specification is preserved: static_assert(!noexcept(fifty_minus(3))); static_assert(noexcept(member_minus(3))); // Binding of a lambda: auto plus = [](int a, int b) { return a + b; }; auto forty_plus = std::bind_front(plus, 40); assert(forty_plus(7) == 47); // equivalent to: plus(40, 7) == 47 #if __cpp_lib_bind_front >= 202306L auto fifty_minus_cpp26 = std::bind_front(50); assert(fifty_minus_cpp26(3) == 47); auto member_minus_cpp26 = std::bind_front<&S::minus>(S{50}); assert(member_minus_cpp26(3) == 47); auto forty_plus_cpp26 = std::bind_front(40); assert(forty_plus(7) == 47); #endif #if __cpp_lib_bind_back >= 202202L auto madd = [](int a, int b, int c) { return a * b + c; }; auto mul_plus_seven = std::bind_back(madd, 7); assert(mul_plus_seven(4, 10) == 47); //: madd(4, 10, 7) == 47 #endif #if __cpp_lib_bind_back >= 202306L auto mul_plus_seven_cpp26 = std::bind_back(7); assert(mul_plus_seven_cpp26(4, 10) == 47); #endif }
[edit] References
C++26 standard (ISO/IEC 14882:2026):
TBD Function templates bind_front and bind_back [func.bind.partial]
C++23 standard (ISO/IEC 14882:2024):
22.10.14 Function templates bind_front and bind_back [func.bind.partial]
C++20 standard (ISO/IEC 14882:2020):
20.14.14 Function template bind_front [func.bind.front]
[edit] See also
| | binds one or more arguments to a function object (function template) [edit] | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | | creates a function object out of a pointer to a member (function template) [edit] |