std::bind
定义于头文件 <functional> |
||
template< class F, class... Args > /* unspecified */ bind( F&& f, Args&&... args ); |
(1) | (自 C++11 起) (constexpr 自 C++20 起) |
template< class R, class F, class... Args > /* unspecified */ bind( F&& f, Args&&... args ); |
(2) | (自 C++11 起) (constexpr 自 C++20 起) |
函数模板 std::bind
为 f 生成一个转发调用包装器。调用此包装器等效于使用绑定到 args 的某些参数绑定来调用 f。
如果 std::is_constructible<std::decay<F>::type, F>::value 为 false,或者 std::is_constructible<std::decay<Arg_i>::type, Arg_i>::value 对于 Args
中的任何类型 Arg_i
均为 false,则程序是非良构的。
如果 std::decay<Ti>::type 或 Args
中的任何类型不是 可移动构造 (MoveConstructible) 或 可析构 (Destructible),则行为未定义。
内容 |
[编辑] 参数
f | - | 可调用 (Callable) 对象(函数对象、函数指针、函数引用、成员函数指针或数据成员指针),它将被绑定到某些参数 |
args | - | 要绑定的参数列表,其中未绑定的参数由命名空间 std::placeholders 的占位符 _1、_2、_3... 替换 |
[编辑] 返回值
未指定类型 T
的函数对象 g,对于它 std::is_bind_expression<T>::value 为 true。它具有以下成员
std::bind 返回类型
成员对象
std::bind
的返回类型保存一个类型为 std::decay<F>::type 的成员对象(从 std::forward<F>(f) 构造),以及每个 args... 一个对象,其类型为 std::decay<Arg_i>::type,类似地从 std::forward<Arg_i>(arg_i) 构造。
构造函数
如果 std::bind
的返回类型的所有成员对象(如上所述)都是可复制构造 (CopyConstructible) 的,则它是可复制构造 (CopyConstructible) 的,否则是可移动构造 (MoveConstructible) 的。该类型定义了以下成员
成员类型
|
(直到 C++20) |
成员函数 operator()
当在函数调用表达式 g(u1, u2, ... uM) 中调用 g 时,会发生存储对象的调用,如同通过
INVOKE
(fd, std::forward<V1>(v1), std::forward<V2>(v2), ..., std::forward<VN>(vN)),或INVOKE<R>
(fd, std::forward<V1>(v1), std::forward<V2>(v2), ..., std::forward<VN>(vN)),其中 fd 是类型为 std::decay<F>::type 的值,绑定参数 v1,
v2, ...,
vN 的值和类型如下面指定。
如果在对 g() 的调用中提供的一些参数与存储在 g 中的任何占位符不匹配,则未使用的参数将被求值并丢弃。
如果底层 INVOKE
操作是非抛出 (non-throwing) 的,则 operator() 的调用是非抛出的 或是一个常量子表达式(自 C++20 起),当被视为未求值操作数时,仅当 INVOKE
操作形式良好时,operator() 才参与重载解析。
如果 g 是 volatile 限定的,则程序是非良构的。
如果 INVOKE
(fd, w1, w2, ..., wN) 对于任何可能的值 w1,
w2, ...,
wN 永远不能是有效的表达式,则行为未定义。
[编辑] 绑定参数
对于每个存储的参数 arg_i,INVOKE
或 INVOKE<R>
操作中对应的绑定参数 v_i 确定如下
[编辑] 情况 1:引用包装器
如果 arg_i 的类型为 std::reference_wrapper<T> (例如,在对 std::bind
的初始调用中使用了 std::ref 或 std::cref),则 v_i 是 arg_i.get(),其类型 V_i
是 T&
:存储的参数通过引用传递到调用的函数对象中。
[编辑] 情况 2:bind 表达式
如果 arg_i 的类型为 T
,对于它 std::is_bind_expression<T>::value 为 true (例如,另一个 std::bind
表达式直接传递到对 std::bind
的初始调用中),则 std::bind
执行函数组合:子表达式不会传递 绑定子表达式将返回的函数对象,而是急切地调用子表达式,并且将其返回值传递给外部可调用对象。如果绑定子表达式有任何占位符参数,它们将与外部绑定共享(从 u1,
u2, ...
中挑选出来)。具体来说,v_i 是 arg_i(std::forward<Uj>(uj)...),其类型 V_i
是 std::result_of<T cv &(Uj&&...)>::type&&(直到 C++17)std::invoke_result_t<T cv &, Uj&&...>&&(自 C++17 起)(cv 限定与 g 的相同)。
[编辑] 情况 3:占位符
如果 arg_i 的类型为 T
,对于它 std::is_placeholder<T>::value 不为 0 (意味着,占位符,例如 std::placeholders::_1, _2, _3, ...
用作对 std::bind
的初始调用的参数),则占位符指示的参数(_1 为 u1,_2 为 u2 等)将传递给可调用对象:v_i 是 std::forward<Uj>(uj),其类型 V_i
是 Uj&&
。
[编辑] 情况 4:普通参数
否则,arg_i 作为左值参数传递给可调用对象:v_i 只是 arg_i,其类型 V_i
是 T
cv &
,其中 cv 与 g 的 cv 限定相同。
[编辑] 异常
仅当从 std::forward<F>(f) 构造 std::decay<F>::type 抛出异常,或者从对应的 std::forward<Arg_i>(arg_i) 为 std::decay<Arg_i>::type 构造的任何构造函数抛出异常时,才会抛出异常,其中 Arg_i
是第 i 个类型,而 arg_i 是 Args... args
中的第 i 个参数。
[编辑] 注意
如可调用 (Callable)中所述,当调用非静态成员函数指针或非静态数据成员指针时,第一个参数必须是对对象的引用或指针(可能包括智能指针,例如 std::shared_ptr 和 std::unique_ptr),将访问该对象的成员。
要绑定的参数将被复制或移动,除非包装在 std::ref 或 std::cref 中,否则永远不会通过引用传递。
允许在同一绑定表达式中重复占位符(例如,多个 _1),但仅当对应的参数 (u1) 是左值或不可移动的右值时,结果才是明确定义的。
[编辑] 示例
#include <functional> #include <iostream> #include <memory> #include <random> void f(int n1, int n2, int n3, const int& n4, int n5) { std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << '\n'; } int g(int n1) { return n1; } struct Foo { void print_sum(int n1, int n2) { std::cout << n1 + n2 << '\n'; } int data = 10; }; int main() { using namespace std::placeholders; // for _1, _2, _3... std::cout << "1) argument reordering and pass-by-reference: "; int n = 7; // (_1 and _2 are from std::placeholders, and represent future // arguments that will be passed to f1) auto f1 = std::bind(f, _2, 42, _1, std::cref(n), n); n = 10; f1(1, 2, 1001); // 1 is bound by _1, 2 is bound by _2, 1001 is unused // makes a call to f(2, 42, 1, n, 7) std::cout << "2) achieving the same effect using a lambda: "; n = 7; auto lambda = [&ncref = n, n](auto a, auto b, auto /*unused*/) { f(b, 42, a, ncref, n); }; n = 10; lambda(1, 2, 1001); // same as a call to f1(1, 2, 1001) std::cout << "3) nested bind subexpressions share the placeholders: "; auto f2 = std::bind(f, _3, std::bind(g, _3), _3, 4, 5); f2(10, 11, 12); // makes a call to f(12, g(12), 12, 4, 5); std::cout << "4) bind a RNG with a distribution: "; std::default_random_engine e; std::uniform_int_distribution<> d(0, 10); auto rnd = std::bind(d, e); // a copy of e is stored in rnd for (int n = 0; n < 10; ++n) std::cout << rnd() << ' '; std::cout << '\n'; std::cout << "5) bind to a pointer to member function: "; Foo foo; auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1); f3(5); std::cout << "6) bind to a mem_fn that is a pointer to member function: "; auto ptr_to_print_sum = std::mem_fn(&Foo::print_sum); auto f4 = std::bind(ptr_to_print_sum, &foo, 95, _1); f4(5); std::cout << "7) bind to a pointer to data member: "; auto f5 = std::bind(&Foo::data, _1); std::cout << f5(foo) << '\n'; std::cout << "8) bind to a mem_fn that is a pointer to data member: "; auto ptr_to_data = std::mem_fn(&Foo::data); auto f6 = std::bind(ptr_to_data, _1); std::cout << f6(foo) << '\n'; std::cout << "9) use smart pointers to call members of the referenced objects: "; std::cout << f6(std::make_shared<Foo>(foo)) << ' ' << f6(std::make_unique<Foo>(foo)) << '\n'; }
输出
1) argument reordering and pass-by-reference: 2 42 1 10 7 2) achieving the same effect using a lambda: 2 42 1 10 7 3) nested bind subexpressions share the placeholders: 12 12 12 4 5 4) bind a RNG with a distribution: 0 1 8 5 5 2 0 7 7 10 5) bind to a pointer to member function: 100 6) bind to a mem_fn that is a pointer to member function: 100 7) bind to a pointer to data member: 10 8) bind to a mem_fn that is a pointer to data member: 10 9) use smart pointers to call members of the referenced objects: 10 10
[编辑] 缺陷报告
以下行为更改缺陷报告被追溯应用于先前发布的 C++ 标准。
DR | 应用于 | 已发布行为 | 正确行为 |
---|---|---|---|
LWG 2021 | C++11 | 1. 绑定参数 未转发到 fd 2. 在情况 2 中, V_i 的类型为std::result_of<T cv (Uj...)>::type |
1. 已转发 2. 已更改为 std::result_of<T cv &(Uj&&...)>::type&& |
[编辑] 参见
(C++20)(C++23) |
按顺序将可变数量的参数绑定到函数对象 (函数模板) |
(C++11) |
std::bind 表达式中未绑定参数的占位符(常量) |
(C++11) |
从成员指针创建函数对象 (函数模板) |