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) 前 (3,4) 后 sizeof...(Args) 个形参绑定到 args 来调用可调用目标。
f 的副本。std::invoke(f, bound_args..., call_args...)。std::invoke(ConstFn, bound_args..., call_args...)。std::invoke(f, call_args..., bound_args...)。std::invoke(ConstFn, call_args..., bound_args...)。下列条件都必须为 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>> && ...)。
参数
| f | - | 将绑定到某些实参的可调用 (Callable) 对象(函数对象、函数指针、函数引用、成员函数指针或数据成员指针) |
| args | - | 要绑定到可调用目标的 (1,2) 前或 (3,4) 后 sizeof...(Args) 个形参的实参的列表
|
| 类型要求 | ||
- 必须满足可移动构造 (MoveConstructible) 。
| ||
- 必须满足可移动构造 (MoveConstructible) 。
| ||
- 必须满足可调用 (Callable) 。
| ||
返回值
类型为未指定的 T 的函数对象(调用包装器),但通过相同实参两次调用 std::bind_front 或 std::bind_back 返回的对象的类型相同。
令 bind-partial 表示 std::bind_front 或 std::bind_back。
返回的对象有下列属性:
bind-partial 的返回类型
成员对象
返回的对象表现为如同它持有:
std::forward<F>(f) 直接非列表初始化的一个 std::decay_t<F> 类型的成员对象 fd,以及std::tuple<std::decay_t<Args>...>(std::forward<Args>(args)...) 构造的 std::tuple 对象 tup,但未指定返回对象的赋值行为,且其名称仅用于阐释。构造函数
bind-partial 的返回类型表现为其复制/移动构造函数进行逐成员复制/移动。若其所有成员对象(指定如上)都为可复制构造 (CopyConstructible) )则它为可复制构造 (CopyConstructible) ,否则为可移动构造 (MoveConstructible) 。
成员函数 operator()
给定先前调用 (1,3) bind-partial(f,args...) 或 (2,4) bind-partial<ConstFn>(args...) 获得的对象 G,在函数调用表达式 g(call_args...} 中调用指定 G 的泛左值 g 时,就会发生对存储的对象的调用,如同:
std::invoke(g.fd, std::get<Ns>(g.tup)..., call_args...),如果 bind-partial 是 std::bind_front,std::invoke(ConstFn, std::get<Ns>(g.tup)..., call_args...),如果 bind-partial 是 std::bind_front,std::invoke(g.fd, call_args..., std::get<Ns>(g.tup)...),如果 bind-partial 是 std::bind_back,std::invoke(ConstFn, call_args..., std::get<Ns>(g.tup)...),如果 bind-partial 是 std::bind_back,其中:
Ns是整数包0, 1, ..., (sizeof...(Args) - 1),g若在调用表达式中是左值,则在 std::invoke 表达式中是左值,否则是右值。从而std::move(g)(call_args...)能够把绑定的实参移动到调用中,而g(call_args...)会复制。
若 g 拥有 volatile 限定的类型则程序非良构。
若其所调用的 std::invoke 表达式为 noexcept,则成员 operator() 为 noexcept(换言之,它保持底层调用运算符的异常说明)。
异常
注解
有意令此类函数取代 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)
|
可能的实现
| (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 { /* 与上面相同 */ }
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)...);
};
}
|
示例
#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); // 等价于: 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 说明被保留:
static_assert(!noexcept(fifty_minus(3)));
static_assert(noexcept(member_minus(3)));
// 绑定 lambda:
auto plus = [](int a, int b) { return a + b; };
auto forty_plus = std::bind_front(plus, 40);
assert(forty_plus(7) == 47); // 等价于: 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
}
引用
- C++26 标准(ISO/IEC 14882:2026):
- TBD Function templates bind_front and bind_back [func.bind.partial]
- C++23 标准(ISO/IEC 14882:2024):
- 22.10.14 Function templates bind_front and bind_back [func.bind.partial]
- C++20 标准(ISO/IEC 14882:2020):
- 20.14.14 Function template bind_front [func.bind.front]
参阅
(C++11) |
绑定一或多个实参到函数对象 (函数模板) |
(C++11) |
从成员指针创建出函数对象 (函数模板) |