std:: bind
|
定义于头文件
<functional>
|
||
|
template
<
class
F,
class
...
Args
>
/* 未指定类型 */ bind ( F && f, Args && ... args ) ; |
(1) |
(C++11 起)
(C++20 起为 constexpr) |
|
template
<
class
R,
class
F,
class
...
Args
>
/* 未指定类型 */ bind ( F && f, Args && ... args ) ; |
(2) |
(C++11 起)
(C++20 起为 constexpr) |
函数模板
std::bind
会为
f
生成一个转发调用包装器。调用该包装器等效于将
f
的部分参数
绑定
到
args
后调用该函数。
如果
std::
is_constructible
<
std::
decay
<
F
>
::
type
, F
>
::
value
为
false
,或者对于
Args
中的任意类型
Arg_i
,
std::
is_constructible
<
std::
decay
<
Arg_i
>
::
type
, Arg_i
>
::
value
为
false
,则程序非良构。
如果
std::
decay
<
Ti
>
::
type
或
Args
中的任何类型不满足
可移动构造
或
可析构
要求,则行为未定义。
目录 |
参数
| f | - | 可调用对象(函数对象、函数指针、函数引用、成员函数指针或数据成员指针),将被绑定到某些参数 |
| 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
返回类型的所有成员对象(如上所述)均为可复制构造,则该类型为
可复制构造
;否则为
可移动构造
。该类型定义了以下成员:
成员类型
|
(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
操作不抛出异常
或是常量子表达式
(C++20起)
时,对
operator
(
)
的调用才
不抛出异常
。
operator
(
)
仅在
INVOKE
操作作为未求值操作数时格式正确的情况下参与重载决议。
如果 g 具有 volatile 限定,则程序非良构。
如果对于任何可能的
w1
,
w2
, ...,
wN
值,
INVOKE
(
fd, w1, w2, ..., wN
)
永远不能是合法表达式,则行为未定义。
绑定参数
对于每个存储的参数
arg_i
,在
INVOKE
或
INVOKE<R>
操作中对应的绑定参数
v_i
按以下方式确定:
情形 1:reference wrappers
如果
arg_i
的类型是
std::
reference_wrapper
<
T
>
(例如,在初始调用
std::bind
时使用了
std::ref
或
std::cref
),那么
v_i
就是
arg_i.
get
(
)
,其类型
V_i
为
T&
:存储的参数通过引用传递给被调用的函数对象。
情形 2:绑定表达式
如果
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:placeholders
如果
arg_i
的类型为
T
,且满足
std::
is_placeholder
<
T
>
::
value
不为
0
(即初始调用
std::bind
时使用了占位符如
std::placeholders::_1, _2, _3, ...
作为参数),则会将占位符指示的参数(
_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 ),指向将要访问其成员的对象。
传递给 bind 的参数会被复制或移动,除非使用 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) 参数重排序和按引用传递: "; int n = 7; // (_1 和 _2 来自 std::placeholders,代表将来会传递给 f1 的参数) auto f1 = std::bind(f, _2, 42, _1, std::cref(n), n); n = 10; f1(1, 2, 1001); // 1 被 _1 绑定,2 被 _2 绑定,1001 未使用 // 调用 f(2, 42, 1, n, 7) std::cout << "2) 使用 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); // 等同于调用 f1(1, 2, 1001) std::cout << "3) 嵌套 bind 子表达式共享占位符: "; auto f2 = std::bind(f, _3, std::bind(g, _3), _3, 4, 5); f2(10, 11, 12); // 调用 f(12, g(12), 12, 4, 5); std::cout << "4) 将 RNG 与分布绑定: "; std::default_random_engine e; std::uniform_int_distribution<> d(0, 10); auto rnd = std::bind(d, e); // e 的副本存储在 rnd 中 for (int n = 0; n < 10; ++n) std::cout << rnd() << ' '; std::cout << '\n'; std::cout << "5) 绑定到成员函数指针: "; Foo foo; auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1); f3(5); std::cout << "6) 绑定到成员函数的 mem_fn: "; 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) 绑定到数据成员指针: "; auto f5 = std::bind(&Foo::data, _1); std::cout << f5(foo) << '\n'; std::cout << "8) 绑定到数据成员的 mem_fn: "; auto ptr_to_data = std::mem_fn(&Foo::data); auto f6 = std::bind(ptr_to_data, _1); std::cout</
缺陷报告
下列行为变更缺陷报告被追溯应用于先前发布的C++标准。
| 缺陷报告 | 应用于 | 发布时行为 | 正确行为 |
|---|---|---|---|
| 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)
|
从成员指针创建函数对象
(函数模板) |