任何定义了函数调用操作符的对象都是函数对象。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 起) |
函数模板
参数
f | - | 可调用 (Callable) 对象(函数对象、指向函数指针、到函数引用、指向成员函数指针或指向数据成员指针) |
args | - | 要绑定的参数列表,未绑定参数为命名空间 |
返回值
未指定类型
std::bind 返回类型
成员对象
构造函数
若
成员类型
|
(C++20 前) |
成员函数 operator()
给定从先前到
- 若存储的参数
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 限定。
若提供于到
若
异常
仅若从 std::forward<F>(f) 构造 std::decay<F>::type 抛出,或从 std::forward<Arg_i>(arg_i) 构造对应的任何 std::decay<Arg_i>::type 抛出才抛出异常,其中
注意
如可调用 (Callable) 中描述,调用指向非静态成员函数指针或指向非静态数据成员指针时,首参数必须是引用或指针(可以包含智能指针,如 std::shared_ptr 与 std::unique_ptr),指向将访问其成员的对象。
到 bind 的参数被复制或移动,而且决不按引用传递,除非包装于 std::ref 或 std::cref 。
允许同一 bind 表达式中的多重占位符(例如多个
调用示例
#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}