std::bind

本文详细介绍了C++11中std::bind和std::function的功能和使用方法,包括如何绑定参数到可调用对象,产生新的可调用实体,以及std::function如何封装各种可调用实体。

bind是这样一种机制,它可以将参数绑定于可调用对象,产生一个新的可调用实体,这种机制在函数回调时颇为有用

https://blog.youkuaiyun.com/caoshangpa/article/details/79173351

std::bind

参考资料


• cplusplus.comhttp://www.cplusplus.com/reference/functional/bind/

• cppreference.comhttp://en.cppreference.com/w/cpp/utility/functional/bind

std::bind简介


• 函数模板声明

复制代码

// cplusplus.com
// simple(1)
template <class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);

// with return type (2)
template <class Ret, class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);

// GCC 4.8.2 - /usr/include/c++/4.8.2/tr1
template <typename _Functor, typename... _ArgTypes>
inline _Bind<typename _Maybe_wrap_member_pointer<_Functor>::type(_ArgTypes...)>
bind(_Functor __f, _ArgTypes... __args)
{
    ...
}

template <typename _Result, typename _Functor, typename... _ArgTypes>
inline _Bind_result<_Result, typename _Maybe_wrap_member_pointer<_Functor>::type (_ArgTypes...)>
bind(_Functor __f, _ArgTypes... __args)
{
    ...
}

// MS C++ 2013 \Microsoft Visual Studio 12.0\VC\include\functional
template <class _Fun, class... _Types>
inline _Bind<false, void, _Fun, _Types...>
bind(_Fun && _Fx, _Types &&... _Args)
{ // bind a function object
    ...
}

template <class _Rx, class... _Ftypes, class... _Types>
inline _Bind<true, _Rx, _Rx (* const)(_Ftypes...), _Types...>
bind(_Rx (*_Pfx)(_Ftypes...), _Types&&... _Args)
{ // bind a function pointer
    ...
}

template <class _Rx, class _Farg0, class... _Types>
inline _Bind<false, void, _Pmd_wrap<_Rx _Farg0::*, _Rx, _Farg0>, _Types...>
bind(_Rx _Farg0::* const _Pmd, _Types&&... _Args)
{ // bind a wrapped member object pointer
    ...
}

#define _IMPLICIT_PMF_WRAP(CALL_OPT, X1, CV_OPT) \
template <class _Rx, class _Farg0, class... _Ftypes, class... _Types> \
inline _Bind<true, _Rx, _Pmf_wrap<_Rx(CALL_OPT _Farg0::*)(_Ftypes...) CV_OPT, _Rx, _Farg0, _Ftypes...>, _Types...> \
bind(_Rx(CALL_OPT _Farg0::* const _Pmf)(_Ftypes...) CV_OPT, _Types&&... _Args) \
{ /* bind a wrapped CV_OPT member function pointer */ \
    ...
}

_MEMBER_CALL_CV(_IMPLICIT_PMF_WRAP, )
...

复制代码

• 函数模板说明

       以cplusplus.com中描述的原型说明:

       基于Fn参数返回一个函数对象,并且以Args参数绑定为函数对象的参数。每个参数要么绑定一个参数值,要么绑定为一个std::placeholders。如果参数绑定成一个值,那么返回的函数对象将总使用绑定的参数值做为调用参数,即调用传入参数将不起作用;如果参数绑定为std::placeholders,那么返回的函数对象在被调用时需要传入实时参数,参数填充的位置即由placeholder指定的序号。

       bind函数返回的函数对象类型和Fn一致,除非用户在Ret参数中指定了返回类型。需要注意的是,Ret参数只是一个模板参数,它并不能由传入该函数的参数进行隐式推导。

• 模板参数说明

       以cplusplus.com中描述的原型说明:

       Fn    :  函数对象、普通函数指针或类成员函数指针。

       Args : 用于绑定的参数列表。其中每个参数要么是参数值要么是一个placeholder

std::bind详解


• 绑定普通函数

复制代码

#include <iostream>
#include <functional>
using namespace std;

int g_Minus(int i, int j)
{
    return i - j;
}

int main()
{
    function<int(int, int)> f1 = bind(g_Minus, 1, 2);
    function<int()>         f2 = bind(g_Minus, 1, 2);                   // 绑定参数返回的函数对象实际等同这种形式
    function<int(int, int)> f3 = bind(g_Minus, placeholders::_1, placeholders::_2);
    function<int(int)>      f4 = bind(g_Minus, 1, placeholders::_1);    // 绑定第一个参数
    function<int(int)>      f5 = bind(g_Minus, placeholders::_1, 1);    // 绑定第二个参数

    cout << f1(3, 2) << endl;                                           // -1,实际传入参数将不起作用
    cout << f2()     << endl;                                           // -1
    cout << f3(3, 2) << endl;                                           // 1
    cout << f4(3)    << endl;                                           // -2
    cout << f5(3)    << endl;                                           // 2

    return 1;
}

复制代码

• 绑定模板函数

复制代码

#include <iostream>
#include <functional>
using namespace std;

template <class T>
T g_Minus(T i, T j)
{
    return i - j;
}

int main()
{
    function<int(int, int)> f1 = bind(g_Minus<int>, 1, 2);
    function<int()>         f2 = bind(g_Minus<int>, 1, 2);                  // 绑定参数返回的函数对象实际等同这种形式
    function<int(int, int)> f3 = bind(g_Minus<int>, placeholders::_1, placeholders::_2);
    function<int(int)>      f4 = bind(g_Minus<int>, 1, placeholders::_1);   // 绑定第一个参数
    function<int(int)>      f5 = bind(g_Minus<int>, placeholders::_1, 1);   // 绑定第二个参数

    cout << f1(3, 2) << endl;                                               // -1,实际传入参数将不起作用
    cout << f2()     << endl;                                               // -1
    cout << f3(3, 2) << endl;                                               // 1
    cout << f4(3)    << endl;                                               // -2
    cout << f5(3)    << endl;                                               // 2

    return 1;
}

复制代码

• 绑定lambda表达式

复制代码

#include <iostream>
#include <functional>
using namespace std;

int main()
{
    function<int(int, int)> f = bind([](int i, int j){ return i - j; }, placeholders::_1, placeholders::_2);
    cout << f(2, 3) << endl;                                            // -1
    return 1;
}

复制代码

• 绑定函数对象

复制代码

#include <iostream>
#include <functional>
using namespace std;

struct Minus
{
    int operator() (int i, int j)
    {
        return i - j;
    }
};

int main()
{
    function<int(int, int)> f = bind(Minus(), placeholders::_1, placeholders::_2);
    cout << f(2, 3) << endl;                                            // -1
    return 1;
}

复制代码

• 绑定类静态成员函数

复制代码

#include <iostream>
#include <functional>
using namespace std;

class Math
{
public:
    static int Minus(int i, int j)
    {
        return i - j;
    }
};

int main()
{
    function<int(int, int)> f = bind(&Math::Minus, placeholders::_1, placeholders::_2);
    cout << f(2, 3) << endl;                                            // -1
    return 1;
}

复制代码

• 绑定类对象成员函数

复制代码

#include <iostream>
#include <functional>
using namespace std;

class Math
{
public:
    int Minus(int i, int j)
    {
        return i - j;
    }
};

int main()
{
    Math m;
    function<int(int, int)> f = bind(&Math::Minus, &m, placeholders::_1, placeholders::_2);
    cout << f(2, 3) << endl;                                            // -1
    return 1;
}

复制代码

• 返回值的类型转换

复制代码

#include <iostream>
#include <functional>
using namespace std;

class Math
{
public:
    int Minus(int i, int j)
    {
        return i - j;
    }
};

struct Result
{
    int m_Result;

    Result() : m_Result(0) {}
    Result(int result) : m_Result(result) {}
};

int main()
{
    Math m;
    auto   f = bind<Result>(&Math::Minus, &m, placeholders::_1, placeholders::_2);
    Result r = f(2, 3);
    cout << r.m_Result << endl;                                         // -1
    return 1;
}

 

C++11新特性之七:bind和function

2018年01月26日 19:52:00 灿哥哥 阅读数:998

版权声明:本文为灿哥哥http://blog.youkuaiyun.com/caoshangpa原创文章,转载请标明出处。 https://blog.youkuaiyun.com/caoshangpa/article/details/79173351

一.std::bind

bind是这样一种机制,它可以将参数绑定于可调用对象,产生一个新的可调用实体,这种机制在函数回调时颇为有用。C++98中,有两个函数bind1st和bind2nd,它们分别用来绑定functor的第一个和第二个参数,都只能绑定一个参数。C++98提供的这些特性已经由于C++11的到来而过时,由于各种限制,我们经常使用bind而非bind1st和bind2nd。在C++11标准库中,它们均在functional头文件中。而C++STL很大一部分由Boost库扩充,STL中的shared_ptr等智能指针,bind及function都是由Boost库引入。在写代码过程中,要养成使用bind,function,lambda和智能指针的习惯,它们非常强大简洁实用。

1.过时的bind1st和bind2nd
bind1st(op, arg) :op(arg, param) 
bind2nd(op, arg) :op(param, arg)

 
  1. vector<int> coll {1, 2, 3, 4, 5, 11, 22, 5, 12};

  2. // 查找第一个元素值大于10的元素

  3. std::find_if(coll.begin(), coll.end(), // 范围

  4. std::bind2nd(std::greater<int>(), 10));// 将10绑定到第二个参数,也就是 ......大于10

  5. // 查找元素值大于10的元素的个数

  6. int _count = count_if(coll.begin(), coll.end(), // 范围

  7. std::bind1st(less<int>(), 10));// 将10绑定到第一个参数,也就是10小于......

2. C++11中的std::bind

 

 
  1. //function object内部调用plus<>(也就是operator+),以占位符(placeholders)_1为第一个参数,

  2. //以10为第二个参数,占位符_1表示实际传入此表达式的第一实参,返回“实参+10”的结果值

  3. auto plus10 = std::bind(std::plus<int> (), std::placeholders::_1, 10);

  4. std::cout << plus10(7) << std::endl;// 输出17

 
  1. // (x + 10)*2,下面的代码中x=7

  2. std::bind(std::multiplies<int>(),

  3. std::bind(std::plus<int>(), std::placeholders::_1, 10),// i+10

  4. 2)(7);

注意:上面所用的less<int>(),greater<int>()以及plus<int>()等都是C++预定义的Function Object。因此我们可以知道,bind可以把参数绑定到“函数对象”上。除此之外,bind()还可以把参数绑定到普通函数、类成员函数、甚至数据成员等。
3. 使用std::bind要注意的地方
(1)bind预先绑定的参数需要传具体的变量或值进去,对于预先绑定的参数,是pass-by-value的。除非该参数被std::ref或者std::cref包装,才pass-by-reference。

(2)对于不事先绑定的参数,需要传std::placeholders进去,从_1开始,依次递增。placeholder是pass-by-reference的;
(3)bind的返回值是可调用实体,可以直接赋给std::function对象; 
(4)对于绑定的指针、引用类型的参数,使用者需要保证在可调用实体调用之前,这些参数是可用的;
(5)类的this可以通过对象或者指针来绑定

4.一个例子

 

 
  1. #include <iostream>

  2. #include <memory>

  3. #include <functional>

  4.  
  5. void f(int n1, int n2, int n3, const int& n4, int n5)

  6. {

  7. std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << '\n';

  8. }

  9.  
  10. int g(int n1)

  11. {

  12. return n1;

  13. }

  14.  
  15. struct Foo {

  16. void print_sum(int n1, int n2)

  17. {

  18. std::cout << n1+n2 << '\n';

  19. }

  20. int data = 10;

  21. };

  22.  
  23. int main()

  24. {

  25. using namespace std::placeholders; // for _1, _2, _3...

  26.  
  27. // demonstrates argument reordering and pass-by-reference

  28. int n = 7;

  29. // (_1 and _2 are from std::placeholders, and represent future

  30. // arguments that will be passed to f1)

  31. auto f1 = std::bind(f, _2, _1, 42, std::cref(n), n);

  32. n = 10;

  33. f1(1, 2, 1001); // 1 is bound by _1, 2 is bound by _2, 1001 is unused

  34. // makes a call to f(2, 1, 42, n, 7)

  35.  
  36. // nested bind subexpressions share the placeholders

  37. auto f2 = std::bind(f, _3, std::bind(g, _3), _3, 4, 5);

  38. f2(10, 11, 12); // makes a call to f(12, g(12), 12, 4, 5);

  39.  
  40. // bind to a pointer to member function

  41. Foo foo;

  42. auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1);

  43. f3(5);

  44.  
  45. // bind to a pointer to data member

  46. auto f4 = std::bind(&Foo::data, _1);

  47. std::cout << f4(foo) << '\n';

  48. }

输出结果;

 

 
  1. 2 1 42 10 7

  2. 12 12 12 4 5

  3. 100

  4. 10

二.std::function

类模版std::function是一种通用、多态的函数封装。std::function的实例可以对任何可以调用的目标实体进行存储、复制、和调用操作,这些目标实体包括普通函数、Lambda表达式、bind表达式、函数指针以及其它函数对象,。std::function对象是对C++中现有的可调用实体的一种类型安全的包装(我们知道像函数指针这类可调用实体,是类型不安全的)。

最简单的理解就是:
通过std::function对C++中各种可调用实体(普通函数、Lambda表达式、函数指针、以及其它函数对象等)的封装,形成一个新的可调用的std::function对象;让我们不再纠结那么多的可调用实体。

 

 
  1. #include <functional>

  2. #include <iostream>

  3.  
  4. struct Foo {

  5. Foo(int num) : num_(num) {}

  6. void print_add(int i) const { std::cout << num_+i << '\n'; }

  7. int num_;

  8. };

  9.  
  10. void print_num(int i)

  11. {

  12. std::cout << i << '\n';

  13. }

  14.  
  15. struct PrintNum {

  16. void operator()(int i) const

  17. {

  18. std::cout << i << '\n';

  19. }

  20. };

  21.  
  22. int main()

  23. {

  24. // store a free function

  25. std::function<void(int)> f_display = print_num;

  26. f_display(-9);

  27.  
  28. // store a lambda

  29. std::function<void()> f_display_42 = []() { print_num(42); };

  30. f_display_42();

  31.  
  32. // store the result of a call to std::bind

  33. std::function<void()> f_display_31337 = std::bind(print_num, 31337);

  34. f_display_31337();

  35.  
  36. // store a call to a member function

  37. std::function<void(const Foo&, int)> f_add_display = &Foo::print_add;

  38. const Foo foo(314159);

  39. f_add_display(foo, 1);

  40. f_add_display(314159, 1);

  41.  
  42. // store a call to a data member accessor

  43. std::function<int(Foo const&)> f_num = &Foo::num_;

  44. std::cout << "num_: " << f_num(foo) << '\n';

  45.  
  46. // store a call to a member function and object

  47. using std::placeholders::_1;

  48. std::function<void(int)> f_add_display2 = std::bind( &Foo::print_add, foo, _1 );

  49. f_add_display2(2);

  50.  
  51. // store a call to a member function and object ptr

  52. std::function<void(int)> f_add_display3 = std::bind( &Foo::print_add, &foo, _1 );

  53. f_add_display3(3);

  54.  
  55. // store a call to a function object

  56. std::function<void(int)> f_display_obj = PrintNum();

  57. f_display_obj(18);

  58. }

输出结果:

 

 
  1. -9

  2. 42

  3. 31337

  4. 314160

  5. 314160

  6. num_: 314159

  7. 314161

  8. 314162

  9. 18


参考链接:http://en.cppreference.com/w/cpp/utility/functional/function

<think>首先,用户的问题是关于C++中的std::bind,询问其使用方法、原理和示例。我需要参考提供的引用内容,因为它们是与主题相关的。 回顾引用内容: - 引用[1]: 描述了std::bind的基本用法,是C++11引入的,用于绑定函数及其参数,生成新的可调用对象,返回std::function对象。 - 引用[2]: 提到std::function和std::bind一起使用,std::function可以存储可调用对象,std::bind用于绑定函数和参数。 - 引用[3]: 解释了std::bind位于<functional>头文件中,用于绑定函数参数或成员函数与对象,生成新的可调用对象。 - 引用[4]: 给出了一个示例:绑定函数f,使用占位符_1,调用时只需一个参数。 用户上一次的问题是我询问std::bind的相关信息,包括使用方法、原理、示例等。所以,我需要结构化回答这些方面。 系统级指令: - 所有行内数学表达式使用$...$格式。但在这个上下文中,是关于C++代码,可能没有数学表达式。如果需要,我会处理。 - 独立公式使用$$...$$并单独成段。同样,可能不适用。 - LaTeX语法正确:确保代码块正确格式。 - 使用中文回答。 - 生成相关问题:在回答结束时,生成相关问题。 - 引用段落末尾添加引用标识:例如[^1]。 回答结构: 1. **介绍std::bind**:简要说明它是什么。 2. **使用方法**:解释如何使用,包括基本语法、绑定普通函数、绑定成员函数、使用占位符等。 3. **原理**:解释其背后的原理,如生成函数对象。 4. **示例**:提供代码示例,演示不同情况的绑定。 5. **总结**:简要总结。 最后,生成相关问题。 确保回答基于引用内容。引用来自站内,所以我需要在相关内容末尾添加引用标识。 例如,如果我说“std::bind返回一个std::function对象[^1]”,则在句末加[^1]。 现在,梳理内容: - 用法:从引用[1]和[4],bind接受函数和参数,可以使用占位符。格式:std::bind(function, arg1, arg2, ..., argN)。占位符如_1, _2等表示参数位置。 - 原理:从引用[3],它生成一个新的可调用对象,存储绑定的函数和参数。 - 示例:从引用[4],auto ff = std::bind(f, _1, 'c', 2.2); 然后调用ff(10)等同于f(10, 'c', 2.2)。 我还应该包括绑定成员函数的情况,因为引用[3]提到可以将成员函数和对象绑定。 代码示例需要使用代码块,如示例中所示。 使用中文回答。 生成相关问题:类似示例,如“std::bind的性能如何?”、“std::bind与lambda表达式的区别?”等。 结构化回答: 1. **引言**:介绍std::bind是什么。 2. **使用方法**:详细步骤。 3. **原理**:解释工作机制。 4. **示例**:提供多个示例代码。 5. **结论**:总结优点。 6. **相关问题**:列出相关问题。 在相关内容添加引用。 </think>### C++ std::bind 使用方法、原理及示例 在 C++ 编程中,`std::bind` 是 C++11 引入的一个强大工具,位于 `<functional>` 头文件中。它允许你将函数(或成员函数、函数对象)与其参数绑定,生成一个新的可调用对象(通常是一个 `std::function` 对象)。这在需要延迟函数调用、适配函数接口或存储函数状态时非常有用[^1]。下面我将从使用方法、原理和示例三个方面详细解释。 #### 1. 使用方法 `std::bind` 的基本语法如下: ```cpp auto bound_function = std::bind(target_function, arg1, arg2, ..., argN); ``` - **target_function**:要绑定的原始函数,可以是普通函数、成员函数、lambda 表达式或函数对象。 - **arg1, arg2, ..., argN**:参数列表,可以是具体值或占位符(如 `std::placeholders::_1`, `_2` 等)。占位符表示参数位置,在调用 `bound_function` 时需要提供实际值。 - **返回值**:生成一个新的可调用对象(类型为 `std::function`),可以通过 `operator()` 调用。 关键用法点: - **绑定普通函数**:直接传入函数名和参数。 - **绑定成员函数**:需要绑定对象的指针或引用(使用 `&ClassName::method` 和对象实例),因为成员函数隐含 `this` 指针[^3]。 - **使用占位符**:占位符(如 `_1`, `_2`)允许部分参数在绑定后指定,提高灵活性,例如绑定多个参数时只留一个占位符[^4]。 - **与 `std::function` 搭配**:`std::bind` 的结果可以存储到 `std::function` 对象中,用于统一处理不同可调用对象[^2]。 使用前需包含头文件:`#include <functional>`。 #### 2. 原理 `std::bind` 的原理是基于函数对象的生成机制: - 当调用 `std::bind` 时,它创建一个内部对象(闭包),该对象存储绑定的函数和参数(包括占位符)。 - 生成的可调用对象在调用时(例如 `bound_function(args...)`),会根据占位符的顺序,将实际参数插入到绑定参数的指定位置,然后执行原始函数。 - 例如,如果绑定时使用 `_1` 作为占位符,则调用 `bound_function(x)` 会将 `x` 映射到对应位置。 - 底层实现涉及模板元编程,确保类型安全和高效调用,生成的函数对象可视为一个适配器,简化了函数柯里化(currying)或参数固定[^3][^4]。 优点包括代码复用性高、支持回调机制,但需注意性能开销(微小,取决于绑定复杂度)和与 lambda 表达式的互补性。 #### 3. 示例 下面提供几个代码示例,演示不同场景下的 `std::bind` 应用。所有示例基于 C++11 及以上编译器。 ##### 示例 1: 绑定普通函数并使用占位符 绑定一个多参数函数,固定部分参数,留一个占位符: ```cpp #include <iostream> #include <functional> // 包含 std::bind 和占位符 // 原始函数:计算三个参数的和 int add(int a, char b, double c) { return a + static_cast<int>(b) + static_cast<int>(c); } int main() { // 绑定 add 函数:固定 b 为 'c'(ASCII 值为 99),c 为 2.2,a 使用占位符 _1 auto bound_add = std::bind(add, std::placeholders::_1, 'c', 2.2); // 调用 bound_add,只需提供第一个参数(映射到 a) int result = bound_add(10); // 等同于 add(10, 'c', 2.2) std::cout << "Result: " << result << std::endl; // 输出: Result: 112(10 + 99 + 3,注意 double 被截断) return 0; } ``` 在这个示例中,`std::bind` 生成了一个新函数 `bound_add`,它只需一个参数,其余参数已固定[^4]。 ##### 示例 2: 绑定成员函数和对象 绑定类的成员函数,需要对象实例: ```cpp #include <iostream> #include <functional> class Calculator { public: int multiply(int a, int b) { return a * b; } }; int main() { Calculator calc; // 绑定成员函数:第一个参数是对象指针(&calc),后续是参数(留一个占位符) auto bound_multiply = std::bind(&Calculator::multiply, &calc, std::placeholders::_1, 5); // 调用 bound_multiply,只需提供第一个参数(映射到 a) int result = bound_multiply(10); // 等同于 calc.multiply(10, 5) std::cout << "Result: " << result << std::endl; // 输出: Result: 50 return 0; } ``` 这里,`std::bind` 将成员函数 `multiply` 和对象 `calc` 绑定,生成了一个独立可调用对象[^3]。 ##### 示例 3: 与 lambda 表达式对比 `std::bind` 常用于简化 lambda 表达式场景;以下是等价实现: ```cpp #include <iostream> #include <functional> void printSum(int a, int b) { std::cout << "Sum: " << a + b << std::endl; } int main() { // 使用 std::bind auto bound_print = std::bind(printSum, std::placeholders::_1, 100); bound_print(50); // 输出: Sum: 150 // 等价 lambda 表达式 auto lambda_print = [](int a) { printSum(a, 100); }; lambda_print(50); // 输出: Sum: 150 return 0; } ``` 此示例展示 `std::bind` 如何替代简单 lambda,但在复杂逻辑中 lambda 可能更灵活[^2]。 #### 总结 `std::bind` 是 C++ 中实现函数适配和参数绑定的高效工具,通过生成新的可调用对象,支持部分参数固定、成员函数绑定等场景,提升了代码的模块化和可维护性[^1][^2][^3]。它原理基于闭包和占位符机制,适用于回调、事件处理等模式。注意,在现代 C++ 中,lambda 表达式常作为替代,但 `std::bind` 在处理复杂绑定或遗留代码时仍具优势。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值