zoukankan      html  css  js  c++  java
  • C11简洁之道:函数绑定

    1、  可调用对象

      在C++中,有“可调用对象”这么个概念,那么什么是调用对象呢?有哪些情况?我们来看看:

    • 函数指针;
    • 具有operator()成员函数的类对象(仿函数);
    • 可以被转换为函数指针的类对象;
    • 类成员(函数)指针。

      我们来看代码:

    //函数指针
    
    void func(void)
    {
        //...
    }
    
    struct Foo
    {
        void operator()(void)
        {
            //...
        }
    };
    
    struct Bar
    {
        using fr_t = void(*)(void);
        static void func(void)
        {
            //...
        }
    
        operator fr_t(void)
        {
            return func;
        }
    };
    
    struct A
    {
        int mia;
    
        void mem_func(void)
        {
               //...
        }
    };
    
    int main(void)
    {
        //函数指针
        void(* func_ptr)(void) = &func;
        func_ptr();
    
        //仿函数
        Foo foo;
        foo();
    
        //被转为指针的类对象
        Bar bar;
        bar();
    
        //类成员函数指针
        void (A::*mem_func_ptr)(void) = &A::mem_func;
    
        //类成员指针
        int A::*mem_obj_ptr = &A::mia;
    
        A aa;
        (aa.*mem_func_ptr)();
        aa.*mem_obj_ptr = 123;
    
        return 0;
    }

      上述的对象都是可调用对象,这些对象的类型统称为“可调用类型”。这些可调用对象都具有统一的操作形式,除了类成员指针之外,都是通过括号的方式来进行调用,但是定义的方法比较多,在C++11中增加了std::function来进行函数对象的调用。

    2、  std::function

      std::function是一个可调用对象的包装器,他是一个类模板,可以容纳除了类成员(函数)指针之外的所用可调用对象,通过指定他的模板参数,可以以统一的方式处理函数、函数对象、函数指针,并允许保存或者延迟执行。

      当我们给std::function填入合适的函数签名(即一个函数类型,只需要包括返回值和参数列表)之后,它就变成了一个可以容纳所有这一类调用方式的“函数包装器”。

    #include <iostream>
    #include <functional>
    
    void func(void)
    {
        std::cout << __FUNCTION__ << std::endl;
    }
    
    class Foo
    {
    public:
        static int foo_func(int a)
        {
            std::cout << __FUNCTION__ << "(" << a << ")->: ";
            return a;
        }
    };
    
    class Bar
    {
    public:
    
        int operator()(int a)
        {
            std::cout << __FUNCTION__ << "(" << a << ")->: ";
            return a;
        }
    };
    
    int main(void)
    {
        //绑定一个普通函数
        std::function<void(void)> fr1 = func;
        fr1();
    
        //绑定一个静态成员函数
        std::function<int(int)> fr2 = Foo::foo_func;
        std::cout << fr2(111) << std::endl;
    
        //绑定一个仿函数
        Bar bar;
        fr2 = bar;
        std::cout << fr2(111) << std::endl;
    
        return 0;
    }

      执行结果:

     

      std::function还可以取代函数指针的作用,因为它可以保存函数延迟执行,所以也适合做回调函数。

    #include <iostream>
    #include <functional>
    
    class A
    {
        std::function<void()> callback;
    public:
        A(const std::function<void()> &f) : callback(f){}
    
        void notify(void)
        {
            callback();
        }
    };
    
    class Foo
    {
    public:
        void operator()(void)
        {
            std::cout << __FUNCTION__ << std::endl;
        }
    };
     
    int main(void)
    {
        Foo foo;
        A aa(foo);
        aa.notify();
    
        return 0;
    }

      std::function还可以作为函数入参,比普通函数指针更加灵活和便利。

    #include <iostream>
    #include <functional>
    
    void call_when_event(int x, const std::function<void(int)>& f)
    {
        if(!(x & 1)) //x % 2 == 0
        {
            f(x);
        }
    }
    
    void output(int x)
    {
        std::cout << x << "  ";
    }
    
    int main(void)
    {
        for(int i = 0; i < 10; i++)
        {
            call_when_event(i, output);
        }
    
        std::cout << std::endl;
    
        return 0;
    }

    3、  std::bind绑定器

    3.1 std::bind绑定器

      std::bind用来将可调用对象与起参数一起进行绑定,绑定的结果使用std::function进行保存,并在我们需要调用的时候调用。它主要有两大作用:

    • 将可调用对象和参数绑定成为一个仿函数;
    • 将多元(参数个数为n,n-1)可调用对象转换成一元或者(n-1)元可调用对象,即只绑定部分对象。

      我们来看实际使用:

    #include <iostream>
    #include <functional>
    
    void call_when_event(int x, const std::function<void(int)>& f)
    {
        if(!(x & 1)) //x % 2 == 0
        {
            f(x);
        }
    }
    
    void output(int x)
    {
        std::cout << x << "  ";
    }
    
    void output2(int x)
    {
        std::cout << x + 2 << "  ";
    }
    
    int main(void)
    {
        {
            auto fr = std::bind(output, std::placeholders::_1);
            for(int i = 0; i < 10; i++)
            {
                call_when_event(i, fr);
            }
    
            std::cout << std::endl;
        }
        {
            auto fr = std::bind(output2, std::placeholders::_1);
            for(int i = 0; i < 10; i++)
            {
                call_when_event(i, fr);
            }
    
            std::cout << std::endl;
        }
    
        return 0;
    }

      通过代码我们可以知道std::bind在函数外部通过绑定不同的函数,控制执行结果。这里我们还使用了std::placeholders占位符来绑定函数参数。

    3.2 std::placeholders

      通过std::placeholders占位符绑定函数参数,使得std::bind的使用非常灵活。std::placeholders决定函数占用位置取用输入参数的第几个参数。

    #include <iostream>
    #include <functional>
    
    void output(int x, int y)
    {
        std::cout << x << "  " << y << std::endl;
    }
    
    int main(void)
    {
        std::bind(output, 1, 2)();                                                 //输出:1 2
        std::bind(output, std::placeholders::_1, 2)(1);                         //输出:1 2
        std::bind(output, 2, std::placeholders::_1)(1);                            //输出:2 1
        //std::bind(output, 2, std::placeholders::_2)(1);                         //error,没有第二个参数
        std::bind(output, 2, std::placeholders::_2)(1,2);                         //输出:2 2,第一个参数被抛弃
        std::bind(output, std::placeholders::_1, std::placeholders::_2)(1,2);     //输出:1 2
        std::bind(output, std::placeholders::_2, std::placeholders::_1)(1,2);     //输出:2 1       
    
        return 0;
    }

    3.3 std::bind+std::function

      我们先看一组例子:

    #include <iostream>
    #include <functional>
    
    class A
    {
    public:
        int mi = 0;
    
        void output(int x, int y)
        {
            std::cout << x << "  " << y << std::endl;
        }
    };
    
    int main(void)
    {
        A a;
        std::function<void(int, int)> fr = std::bind(&A::output, &a, std::placeholders::_1, std::placeholders::_2);
        fr(1, 2);   
    
        std::function<int &(void)> fr_i = std::bind(&A::mi, &a);
        fr_i() = 123;
        std::cout << a.mi << std::endl;
    
        return 0;
    }

      fr的类型是std::function<void(int, int)>,我们通过std::bind将A的成员函数output的指针和a绑定,并转换为一个仿函数存储在fr中。

      通过std::bind将A的成员mi的指针和a绑定,返回的结果放入类型为std::function<int &(void)>的fr_i中,可以在需要的时候修改这个成员的值。

    3.4 改善标准函数

      假如我们有一个这样的需求,对某个集合里面的元素进行统计,假设元素类型为int,那么我们需要对类型做比较,必须有一个阀值,即大于或者小于这个数。这里我们可以通过标准库的函数来实现。

    #include <iostream>
    #include <functional>
    
    int main()
    {
        std::vector<int> coll;
        for (int i = 1; i <= 10; ++i)
        {
            coll.push_back(i);
        }
    
        // 查找元素值大于10的元素的个数
        // 也就是使得10 < elem成立的元素个数
        int res = count_if(coll.begin(), coll.end(), std::bind1st(less<int>(), 10));
        cout << res << endl;
    
        // 查找元素值小于10的元素的个数
        // 也就是使得elem < 10成立的元素个数
        res = count_if(coll.begin(), coll.end(), std::bind2nd(less<int>(), 10));
        cout << res << endl;
    
        bool b = less<int>(10, 20); // 返回true
    
        return 0;
    }

      本质上是对一个二元函数less<int>的调用,但是要分别调用bind1st,bind2nd,用起来比较繁杂,现在我们有bind,可以用统一的方式去实现。并不用关心是bind1st还是bind2nd,用bind即可。

    #include <iostream>
    #include <functional>
    
    int main()
    {
        using std::placeholders::_1;
        std::vector<int> coll;
    
        //查找元素值大于10的元素个数
        int count = std::count_if(coll.begin(), coll.end(), std::bind(less<int>(), 10, _1));
    
        //查找元素值小于10的元素个数
        count = std::count_if(coll.begin(), coll.end(), std::bind(less<int>(), _1, 10));
    
        return 0;
    }

    3.5 组合使用

      bind可以绑定多个函数,假设我们要对某个集合在大于5小于10的元素个数进行统计,我们该怎么封装呢?

      首先封装一个判断是否大于5的函数,使其输入只有一个参数,直接和5比较,大于5返回true。

    std::bind(std::greater<int>(), std::placeholders::_1, 5);

      同样,我们需要封装一个判断是否小于10的函数,使其输入一个参数,小于10则返回true。

    std::bind(std::less_equal<int>(), std::placeholders::_1, 10);

      然后组合,即可调用:

    using std::placeholders::_1;
    
    auto f = std::bind(std::logical_and<bool>(),
    
    std::bind(std::greater<int>(), std::placeholders::_1, 5),
    
    std::bind(std::less_equal<int>(), std::placeholders::_1, 10));
    
    int count = std::count_if(coll.begin(), coll.end(), f);
  • 相关阅读:
    函数要多小才够好——谈小函数之道
    vb.net 打字练习
    vb.net 打字练习
    vb.net 打字练习
    unsigned int 与 unsigned long 一样吗?
    epoll使用详解(精髓)
    论epoll的使用 高调coding,低调做人 C++博客
    学习使用epoll The time is passing ITeye技术网站
    ubuntu下sed命令详解 Dicky 开源中国社区
    分享:jquery遍历之children()与find()的区别
  • 原文地址:https://www.cnblogs.com/ChinaHook/p/7658411.html
Copyright © 2011-2022 走看看