实现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));

但是这样存在以下问题:

  1. 无法指定绑定几个
  2. 无法指定绑定值具体绑定到形参的哪个位置
  3. 没有通用性,形参类型可以是各种各样的

实际上,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
}
© 2019 - 2023 · YuYoung's Blog