任何定义了函数调用操作符的对象都是函数对象。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}