std::bind_front, std::bind_back
定义于头文件 <functional> |
||
std::bind_front |
||
template< class F, class... Args > constexpr /* 未指定 */ bind_front( F&& f, Args&&... args ); |
(1) | (C++20 起) |
template< auto ConstFn, class... Args > constexpr /* 未指定 */ bind_front( Args&&... args ); |
(2) | (C++26 起) |
std::bind_back |
||
template< class F, class... Args > constexpr /* 未指定 */ bind_back( F&& f, Args&&... args ); |
(3) | (C++23 起) |
template< auto ConstFn, class... Args > constexpr /* 未指定 */ bind_back( Args&&... args ); |
(4) | (C++26 起) |
函数模板 std::bind_front
和 std::bind_back
生成一个完美转发的调用包装器,允许用其 (1,2) 前 sizeof...(Args) 个参数或 (3,4) 后 sizeof...(Args) 个参数绑定到 args
来调用可调用目标。
f
的一个副本。以下条件必须为 true,否则程序是非良构的
- (1,3) std::is_constructible_v<std::decay_t<F>, F>,
- (1,3) std::is_move_constructible_v<std::decay_t<F>>,
- (2,4) 如果 decltype(ConstFn) 是指针或指向成员的指针,则
ConstFn
不是空指针, - (std::is_constructible_v<std::decay_t<Args>, Args> && ...),
- (std::is_move_constructible_v<std::decay_t<Args>> && ...).
目录 |
[edit] 参数
f | - | 可调用 对象(函数对象、函数指针、函数引用、成员函数指针或数据成员指针),它将被绑定到某些参数 |
args | - | 要绑定到可调用目标 (1,2) 前或 (3,4) 后 sizeof...(Args) 个参数的参数列表 |
类型要求 | ||
-std::decay_t<F> 必须满足 可移动构造 的要求。 | ||
-std::decay_t<Args>... 必须满足 可移动构造 的要求。 | ||
-decltype(ConstFn) 必须满足 可调用 的要求。 |
[edit] 返回值
一个类型为 T
的函数对象(调用包装器),其类型未指定,但两次以相同参数调用 std::bind_front
或 std::bind_back
返回的对象的类型相同。
设 bind-partial
为 std::bind_front
或 std::bind_back
。
返回的对象具有以下属性
bind-partial 返回类型
成员对象
返回的对象表现得像它持有
tup
,用 std::tuple<std::decay_t<Args>...>(std::forward<Args>(args)...) 构造,除了返回对象的赋值行为未指定,且名称仅用于说明。构造函数
bind-partial
的返回类型表现得像其复制/移动构造函数执行成员级复制/移动。如果其所有成员对象(如上所述)是 可复制构造 的,则它是 可复制构造 的;否则它是 可移动构造 的。
成员函数 operator()
给定一个对象 G
,它是由早先调用 (1,3) bind-partial(f, args...)
或 (2,4) bind-partial<ConstFn>(args...)
获得的,当一个指定 G
的左值 g
在函数调用表达式 g(call_args...) 中被调用时,存储对象的调用发生,如同通过
其中
-
Ns
是一个整数包0, 1, ..., (sizeof...(Args) - 1)
, - 如果
g
在调用表达式中是左值,则在 std::invoke 表达式中g
是左值,否则是右值。因此 std::move(g)(call_args...) 可以将绑定的参数移动到调用中,而 g(call_args...) 将会复制。
-
如果 g
具有 volatile 限定类型,则程序是非良构的。
如果其调用的 std::invoke 表达式是 noexcept,则成员 operator() 是 noexcept(换句话说,它保留了底层调用运算符的异常规范)。
[edit] 异常
[edit] 备注
这些函数模板旨在替代 std::bind。与 std::bind
不同,它们不支持任意参数重排,并且对嵌套绑定表达式或 std::reference_wrapper 没有特殊处理。另一方面,它们关注调用包装器对象的价值类别,并传播底层调用运算符的异常规范。
如 std::invoke 中所述,当调用指向非静态成员函数或指向非静态数据成员的指针时,第一个参数必须是引用或指针(包括可能的智能指针,如 std::shared_ptr 和 std::unique_ptr)到将访问其成员的对象。
std::bind_front
或 std::bind_back
的参数被复制或移动,除非包装在 std::ref 或 std::cref 中,否则从不通过引用传递。
通常,使用 (1) std::bind_front
和 (3) std::bind_back
将参数绑定到函数或成员函数需要存储函数指针和参数,即使语言精确地知道要调用哪个函数而无需解引用指针。为了保证这些情况下的“零成本”,C++26 引入了版本 (2,4)(它将可调用对象作为 非类型模板参数 的参数)。
特性测试宏 | 值 | 标准 | 特性 |
---|---|---|---|
__cpp_lib_bind_front |
201907L |
(C++20) | std::bind_front , (1) |
202306L |
(C++26) | 允许将可调用对象作为非类型模板参数传递给 std::bind_front , (2) | |
__cpp_lib_bind_back |
202202L |
(C++23) | std::bind_back , (3) |
202306L |
(C++26) | 允许将可调用对象作为非类型模板参数传递给 std::bind_back , (4) |
[edit] 可能的实现
(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] 示例
#include <cassert> #include <functional> 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<minus>(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<plus>(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<madd>(7); assert(mul_plus_seven_cpp26(4, 10) == 47); #endif }
[edit] 参考文献
- C++26 标准 (ISO/IEC 14882:2026)
- 待定 函数模板 bind_front 和 bind_back [func.bind.partial]
- C++23 标准 (ISO/IEC 14882:2024)
- 22.10.14 函数模板 bind_front 和 bind_back [func.bind.partial]
- C++20 标准 (ISO/IEC 14882:2020)
- 20.14.14 函数模板 bind_front [func.bind.front]
[edit] 参阅
(C++11) |
将一个或多个参数绑定到函数对象 (函数模板) |
(C++11) |
从指向成员的指针创建函数对象 (函数模板) |