任何定义了函数调用操作符的对象都是函数对象。C++ 支持创建、操作新的函数对象,同时也提供了许多内置的函数对象。
std::bind_front 与 std::bind 提供部分函数应用的支持,即绑定参数到函数以创建新函数。
std::bind
template< class F, class... Args > | (1) | (C++11 起) |
template< class R, class F, class... Args > | (2) | (C++11 起) |
函数模板 bind
生成 f
的转发调用包装器。调用此包装器等价于以一些绑定到 args
的参数调用 f
。
f | - | 可调用 (Callable) 对象(函数对象、指向函数指针、到函数引用、指向成员函数指针或指向数据成员指针) |
args | - | 要绑定的参数列表,未绑定参数为命名空间 std::placeholders 的占位符 _1, _2, _3... 所替换 |
未指定类型 T
的函数对象,满足 std::is_bind_expression<T>::value == true 。它有下列属性:
std::bind
的返回类型保有从 std::forward<F>(f) 构造的 std::decay<F>::type 类型成员对象,和对于每个 args...
类型为 std::decay<Arg_i>::type 的各一个对象,类似地从 std::forward<Arg_i>(arg_i) 构造。
若 std::bind
的返回类型的所有成员类型(说明如上)为可复制构造 (CopyConstructible) ,则它为可复制构造 (CopyConstructible) ,否则为可移动构造 (MoveConstructible) 。类型定义下列成员:
成员类型 | (C++20 前) |
operator()
给定从先前到 bind
调用获得的对象 g
,从函数调用表达式 g(u1, u2, ... uM) 调用它时,发生被存储对象的调用,如同以 std::invoke(fd, std::forward<V1>(v1), std::forward<V2>(v2), ..., std::forward<VN>(vN)) ,其中 fd
是 std::decay_t<F> 类型值,受绑定 v1, v2, ..., vN
参数的值和类型按以下方式确定。
arg
拥有类型 std::reference_wrapper<T> (例如,在起始的到 bind
调用中使用了 std::ref 或 std::cref ),则上述 std::invoke
调用中的 vn
是 arg.get()
且同一调用中的类型 Vn
是 T&
:存储的参数按引用传递进入被调用的函数对象。arg
拥有类型 T
并满足 std::is_bind_expression<T>::value == true (例如,直接传递到起始的对 bind
调用的另一 bind
表达式),则 bind
进行函数组合:不是传递该 bind 子表达式将返回的函数对象,而是饥渴地调用该子表达式,并将其返回值传递给外层可调用对象。若 bind 子表达式拥有任何占位符参数,则将它们与外层 bind 共享(从 u1, u2, ...
中选出)。特别是,上述 std::invoke
调用中的参数 vn
是 arg(std::forward<Uj>(uj)...)
而同一调用中的类型 Vn
是 std::result_of_t<T cv &(Uj&&...)>&& ( cv 限定与 g
的相同)。arg
拥有类型 T
并满足 std::is_placeholder<T>::value != 0 (表示以如 std::placeholders::_1, _2, _3, ...
的占位符为到 bind
初始调用的参数),则将占位符所指示的参数( _1
的 u1
、 _2
的 u2
等)传递给可调用对象:上述 std::invoke
调用中的参数 vn
是 std::forward<Uj>(uj) 而同一调用中对应类型 Vn
是 Uj&& 。arg
作为左值参数传递给:上述 std::invoke
调用中的参数 vn
单纯地是 arg
且对应类型 Vn
是 T cv &
,其中 cv 是与 g
相同的 cv 限定。若提供于到 g()
调用的一些参数不匹配存储于 g
的任何占位符,则求值并忽略未使用的参数。
若 g
为 volatile 限定(即其 cv 限定符是 volatile 或 const volatile ),则行为未定义。
仅若从 std::forward<F>(f) 构造 std::decay<F>::type 抛出,或从 std::forward<Arg_i>(arg_i) 构造对应的任何 std::decay<Arg_i>::type 抛出才抛出异常,其中 Arg_i
是 Args... args
中第 i 个类型,而 arg_i
是第 i 个参数。
如可调用 (Callable) 中描述,调用指向非静态成员函数指针或指向非静态数据成员指针时,首参数必须是引用或指针(可以包含智能指针,如 std::shared_ptr 与 std::unique_ptr),指向将访问其成员的对象。
到 bind 的参数被复制或移动,而且决不按引用传递,除非包装于 std::ref 或 std::cref 。
允许同一 bind 表达式中的多重占位符(例如多个 _1
),但结果仅若对应参数( u1
)是左值或不可移动右值才良好定义。
#include <random>
#include <iostream>
#include <memory>
#include <functional>
struct Cell
{
int x;
int y;
Cell() = default;
Cell(int a, int b): x(a), y(b) {}
Cell(const Cell &cell)
{
x = cell.x;
y = cell.y;
}
bool operator <(const Cell &cell) const
{
if (x == cell.x)
{
return y < cell.y;
}
else
{
return x < cell.x;
}
}
Cell &operator+(const Cell &cell)
{
x += cell.x;
y += cell.y;
return *this;
}
void print_sum(Cell cell1, Cell cell2)
{
*this = cell1 + cell2;
std::cout << "{" << x << "," << y << "}" << std::endl;
}
};
std::ostream &operator<<(std::ostream &os, const Cell &cell)
{
os << "{" << cell.x << "," << cell.y << "}";
return os;
}
void Function1(Cell cell1, Cell cell2, Cell cell3, const Cell& cell4, Cell cell5)
{
std::cout << cell1 << ' ' << cell2 << ' ' << cell3 << ' '
<< cell4 << ' ' << cell5 << std::endl;
}
Cell Function2(Cell cell)
{
return cell;
}
struct Foo
{
Cell data = {1024, 1024};
};
int main()
{
using namespace std::placeholders; // 对于 _1, _2, _3...
// 演示参数重排序和按引用传递
Cell cell1 = {101, 101};
// ( _1 与 _2 来自 std::placeholders ,并表示将来会传递给 f1 的参数)
auto function1 = std::bind(Function1, std::placeholders::_2,
Cell{102, 102}, std::placeholders::_1, std::cref(cell1), cell1);
cell1 = {1024, 1024};
// Cell{103, 103} 为 _1 所绑定, Cell{104, 104} 为 _2 所绑定,不使用 Cell{105, 105}
// 进行到 Function1(Cell{104, 104}, Cell{102, 102}, Cell{103, 103}, cell1, Cell{101, 101}) 的调用
function1(Cell{103, 103}, Cell{104, 104}, Cell{105, 105});
// 嵌套 bind 子表达式共享占位符
auto function2 = std::bind(Function1, std::placeholders::_3,
std::bind(Function2, std::placeholders::_3),
std::placeholders::_3, Cell{104, 104}, Cell{105, 105});
// 进行到 Function1(Cell{108, 108}, Function2(Cell{108, 108}),
//Cell{108, 108}, Cell{104, 104}, Cell{105, 105}) 的调用
function2(Cell{106, 106}, Cell{107, 107}, Cell{108, 108});
// 常见使用情况:以分布绑定 RNG
std::default_random_engine e;
std::uniform_int_distribution<> d(0, 10);
std::function<int()> rnd = std::bind(d, e); // e 的一个副本存储于 rnd
for (int n = 0; n < 10; ++n)
{
std::cout << rnd() << ' ';
}
std::cout << std::endl;
// 绑定指向成员函数指针
Cell functionCell = {101, 101};
auto function3 = std::bind(&Cell::print_sum, &functionCell, Cell{106, 106}, Cell{107, 107});
function3(Cell{108, 108});
// 绑定指向数据成员指针
Foo foo;
auto function4 = std::bind(&Foo::data, std::placeholders::_1);
std::cout << function4(foo) << std::endl;
// 智能指针亦能用于调用被引用对象的成员
std::cout << function4(std::make_shared<Foo>(foo)) << std::endl;
return 0;
}
{104,104} {102,102} {103,103} {1024,1024} {101,101}
{108,108} {108,108} {108,108} {104,104} {105,105}
0 1 8 5 5 2 0 7 7 10
{213,213}
{1024,1024}
{1024,1024}