实现std::bind
-目录-
std::bind作用
对函数进行包装,指定某个参数为默认值,返回一个新的函数。使用场景:需要将多个入参的函数包装为少量入参的情况。这个函数可以是函数指针,仿函数等。甚至是类的成员函数和成员变量。本文讨论前两种情况。
例子:
struct my_less {
bool operator()(int a, int b) {
return a < b;
}
};
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for_each(v.begin(), v.end(), my_less());
for_each调用的仿函数一定是入参只有一个的函数。这里传入一个入参个数为2的函数,编译出错!
那么当我们要想指定和某个数比较(比如和5比较),但是仍然想用入参为两个的my_less函数,该怎么办呢?
第一种方法:使用参数默认值
struct my_less {
//指定第二参数默认为5
bool operator()(int a, int b = 5) {
return a < b;
}
};
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for_each(v.begin(), v.end(), my_less());
第二种方法:使用std::bind
struct my_less {
bool operator()(int a, int b) {
return a < b;
}
};
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for_each(v.begin(), v.end(), std::bind(my_less(), std::placeholders::_1, 5));
可以看出第二种方法是第一种的扩展,相当于动态指定了形参默认值。
手写bind
初步思路
可以看到std::bind的参数列表如下:
函数,占位符(或绑定值),占位符(或绑定值)…
一个很简单的思路是进行一次简单的封装,就像这样
struct my_less {
bool operator()(int a, int b) {
return a < b;
}
};
struct my_less_bind {
//构造函数 临时存储第二参数然后直接调用
my_less_bind(int b) : _b(b) {}
bool operator()(int a) {
my_less()(a, _b);
}
int _b;
};
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
for_each(v.begin(), v.end(), my_less_bind(5));
但是这样存在以下问题:
- 无法指定绑定几个
- 无法指定绑定值具体绑定到形参的哪个位置
- 没有通用性,形参类型可以是各种各样的
实际上,std提供的std::bind1st 和 std::bind2nd就是这个思路,但是这两个函数已经弃用。他们只是解决了问题3。
而对于更多的形参和绑定的多样性,GNU推荐使用std::bind。具体用法不再赘述。
解决以上问题的办法:
对于问题1和2,可以考虑绑定时传入所有形参,不绑定的形参以占位符形式也要传入(如std::bind所实现的那样)。传入的形参应当封装成两类:绑定数值和非绑定数值(占位符)。对于绑定数值,在函数调用时使用预设好的绑定数值。而对于非绑定数值调用时传入什么就使用什么。对于问题3,使用泛型就好了。
一个简单的实现
绑定一个值到有三个入参的函数的代码如下:
//定义占位符类型
template<int>
class my_placeholder {
};
//创建占位符对象 之后使用
my_placeholder<1> _1;
my_placeholder<2> _2;
my_placeholder<3> _3;
//使用重载做类型判断
template<typename A>
class list1 {
public:
list1(A a) : a(a) {}
//利用函数重载 根据选择传入的参数类型 返回不同的值 使用什么函数返回无所谓 这里使用operator[]
//参数是第一个占位符类型时 使用绑定的值替换参数 相当于是一个if判断类型
A operator[](my_placeholder<1>) const { return a; }
//参数是非占位符类型 直接返回
template<typename T>
T operator[](T t) const { return t; }
A a;
};
//仿函数
template<typename F, typename A1, typename A2, typename A3>
class my_binder {
public:
my_binder(F f, A1 a, A2 b, A3 c) : f(f), a1(a), a2(b), a3(c) {}
//重载函数调用
template<class T>
void operator()(T t) {
list1<T> l1s(t);
f(l1s[a1], l1s[a2], l1s[a3]);
}
private:
F f;
A1 a1;
A2 a2;
A3 a3;
};
template<typename F, typename A1, typename A2, typename A3>
my_binder<F, A1, A2, A3> my_bind(F f, A1 a1, A2 a2, A3 a3) {
return my_binder<F, A1, A2, A3>(f, a1, a2, a3);
}
struct my_functor {
void operator()(int a, int b, int c) {
std::cout << a << " " << b << " " << c << std::endl;
}
};
int main() {
my_bind(my_functor(), _1, 6, 6)(1); //1 6 6
}