• C++11的std::function和bind绑定器


    可调用对象

            在C++中,存在“可调用对象”这么一个概念。准确来说,可调用对象有如下几种定义:

                    1、是一个函数指针

                    2、是一个具有operator()成员函数的类对象(仿函数)

                    3、是一个可转换为函数指针的类对象

                    4、是一个类成员(函数)指针

    1. #include
    2. using namespace std;
    3. void func(void)
    4. {}
    5. struct Foo
    6. {
    7.     void operator()(void){}
    8. };
    9. struct Bar
    10. {
    11.     using fr_t = void(*)(void);
    12.     static void func(void)
    13.     {
    14.         printf("%s %s %d\n", __FILE__, __func__, __LINE__);
    15.     }
    16.     operator fr_t(void)
    17.     {
    18.         return func;
    19.     }
    20. };
    21. struct A
    22. {
    23.     int a_;
    24.     void mem_func(void)
    25.     {
    26.         printf("%s %s %d\n", __FILE__, __func__, __LINE__);
    27.     }
    28. };
    29. int main()
    30. {
    31.     void (*func_ptr)(void) = &func; ///1、函数指针
    32.     func_ptr();
    33.     Foo foo;
    34.     foo();                          ///2、仿函数
    35.     Bar bar;
    36.     bar();                          ///3、可被转化为函数指针的类对象
    37.     void (A::*mem_func_ptr)(void) = &A::mem_func;   ///4、类成员函数指针
    38.     int A::*mem_obj_ptr = &A::a_;           ///类成员指针
    39.     A aa;
    40.     (aa.*mem_func_ptr)();
    41.     aa.*mem_obj_ptr = 123;
    42.     return 0;
    43. }

            从上述可以看到,除了类成员指针之外,上面定义涉及的对象均为可以像一个函数那样做调用操作。

            在C++11中,像上面例子中的这些对象(func_ptr,foo,bar,mem_func_ptr,mem_obj_ptr)都被称为可调用对象。相对应的,这些对象的类型被统称为“可调用类型”。

            现在,C++11通过std::function和std::bind统一了可调用对象的各自操作。

    可调用对象包装器---std::function

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

    std::function的基本用法

    1. #include
    2. #include
    3. using namespace std;
    4. void func(void)
    5. {
    6.     cout << __FUNCTION__ << endl;
    7. }
    8. class Foo
    9. {
    10. public:
    11.     static int foo_func(int a)
    12.     {
    13.         cout << __FUNCTION__ << "(" << a << ") ->: ";
    14.         return a;
    15.     }
    16. };
    17. class Bar
    18. {
    19. public:
    20.     int operator()(int a)
    21.     {
    22.         cout << __FUNCTION__ << "(" << a << ") ->: ";
    23.         return a;
    24.     }
    25. };
    26. int main()
    27. {
    28.     function<void(void)> fr1 = func;        ///绑定一个普通函数
    29.     fr1();
    30.     ///绑定一个类的静态成员函数
    31.     std::function<int(int)> fr2 = Foo::foo_func;
    32.     cout << fr2(123) << endl;
    33.     Bar bar;
    34.     fr2 = bar;
    35.     cout << fr2(123) << endl;
    36.     return 0;
    37. }

    运行结果如下:

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

    std::function作为回调函数的示例

    1. #include
    2. #include
    3. using namespace std;
    4. class A
    5. {
    6. public:
    7.     A(const std::function<void()> & f):callback_(f){}
    8.     void notify(void)
    9.     {
    10.         callback_();
    11.     }
    12. private:
    13.     std::function<void()> callback_;
    14. };
    15. class Foo
    16. {
    17. public:
    18.     void operator()(void)
    19.     {
    20.         cout << __FUNCTION__ << endl;
    21.     }
    22. };
    23. int main()
    24. {
    25.     Foo foo;
    26.     A aa(foo);
    27.     aa.notify();
    28.     return 0;
    29. }

            从上面的例子中可以看到,std::function可以取代函数指针的作用。因为它可以保存函数延迟执行,所以比较适合作为回调函数。

    std::function作为函数入参示例

    1. #include
    2. #include
    3. using namespace std;
    4. void call_when_even(int x, const std::function<void(int)>& f)
    5. {
    6.     if (!(x & 1))
    7.     {
    8.         f(x);
    9.     }
    10. }
    11. void output(int x)
    12. {
    13.     cout << x << " ";
    14. }
    15. int main()
    16. {
    17.     for(int i = 0; i < 10; i++)
    18.     {
    19.         call_when_even(i, output);
    20.     }
    21.     cout << endl;
    22.     return 0;
    23. }

            从上面的例子中可以看到,std::function比普通函数指针更灵活和便利。

    std::bind绑定器

            std::bind用来将可调用对象与其参数一起进行绑定。绑定后的结果可以使用std::function进行保存,并延迟调用到任何我们需要的时候。

            通俗来讲,它主要有两大作用:

            1、将可调用对象与其参数一起绑定成一个仿函数

            2、将多元(参数个数为n,n>1)可调用对象转成一元或者(n-1)元可调用对象,即只绑定部分参数。

    std::bind基本用法

    1. #include
    2. #include
    3. using namespace std;
    4. void call_when_even(int x, const std::function<void(int)>& f)
    5. {
    6.     if (!(x & 1))
    7.     {
    8.         f(x);
    9.     }
    10. }
    11. void output(int x)
    12. {
    13.     cout << x << " ";
    14. }
    15. void output_add_2(int x)
    16. {
    17.     cout << x + 2 << " ";
    18. }
    19. int main()
    20. {
    21.     {
    22.         auto fr = std::bind(output, std::placeholders::_1);
    23.         for(int i = 0; i < 10; i++)
    24.         {
    25.             call_when_even(i, fr);
    26.         }
    27.         cout << endl;
    28.     }
    29.     {
    30.         auto fr = std::bind(output_add_2, std::placeholders::_1);
    31.         for(int i = 0; i < 10; i++)
    32.         {
    33.             call_when_even(i, fr);
    34.         }
    35.         cout << endl;
    36.     }
    37.     return 0;
    38. }

            我们使用std::bind在函数外部通过绑定不同的函数,控制了最后的执行结果。

            我们使用auto fr保存std::bind的返回结果,因为我们不关心std::bind真正的返回类型(实际上std::bind的返回类型是一个stl内部定义的仿函数类型),只需要知道它是一个仿函数,可以直接赋值给一个std::function。当然,这里直接使用std::function类型来保存std::bind的返回值也是可以的。

            std::placeholders::_1是一个占位符,代表这个位置将在函数调用时,被传入的第一个参数所替代。

    std::bind的占位符

    1. #include
    2. #include
    3. using namespace std;
    4. void output(int x, int y)
    5. {
    6.     cout <<  x << " " << y << endl;
    7. }
    8. int main()
    9. {
    10.     ///输出1,2
    11.     std::bind(output, 1, 2)();
    12.     ///输出2,2
    13.     std::bind(output, std::placeholders::_1, 2)(2);
    14.     ///输出3,444
    15.     std::bind(output, 3, std::placeholders::_1)(444);
    16.     ///error,调用时没有第二个参数
    17.     ///std::bind(output, 3, std::placeholders::_2)(333);
    18.     ///调用时第一个参数被吞掉了
    19.     ///输入1, 333
    20.     std::bind(output, 1, std::placeholders::_2)(7, 333);
    21.     ///输入7, 333
    22.     std::bind(output, std::placeholders::_1, std::placeholders::_2)(7, 333);
    23.     return 0;
    24. }

            上面对std::bind的返回结果直接施以调用。可以看到,std::bind可以直接绑定函数的所有参数,也可以绑定部分参数。

            在绑定部分参数的时候,通过std::placeholders,来决定空位参数将会属于调用发生时的第几个参数。

    std::bind和std::function配合使用

    1. #include
    2. #include
    3. using namespace std;
    4. class A
    5. {
    6. public:
    7.     int i_ = 0;
    8.     void output(int x, int y)
    9.     {
    10.         cout << x << " " << y << endl;
    11.     }
    12. };
    13. int main()
    14. {
    15.     A a;
    16.     std::function<void(int, int)> fr = std::bind(&A::output, &a, std::placeholders::_1, std::placeholders::_2);
    17.     fr(1, 2);
    18.     std::function<int& (void)> fr_i = std::bind(&A::i_, &a);
    19.     fr_i() = 123;
    20.     cout << a.i_ << endl;
    21.     return 0;
    22. }

            fr的类型是std::function。我们通过使用std::bind,将A的成员函数output的指针和a绑定,并转换为一个仿函数放入fr中存储。

            之后,std::bind将A的成员i_的指针和a绑定,返回的结果被放入std::function中存储,并可以在需要时修改访问这个成员。

  • 相关阅读:
    【UE5 C++基础 05】UBT基础
    MVC 三层架构案例详细讲解
    文件的随机读写函数:ftell & rewind
    高性能日志脱敏组件:已支持 log4j2 和 logback 插件
    ubuntu linux C/C++环境搭建
    基于Katz维数的改进谱减算法
    (web前端网页制作课作业)使用HTML+CSS制作非物质文化遗产专题网页设计与实现
    LamdaUpdateWapper失效问题
    Linux 文件系统学习
    IELTS-ACADEMIC-15
  • 原文地址:https://blog.csdn.net/qq_25048473/article/details/133981681