C&C++   发布时间:2022-04-03  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了C++11 std::bind大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

std::bind的基本概念

  • std::bind 是一个函数模板,它就像一个函数适配器,可以把一个原本接收N个参数的函数fn,通过绑定一些参数,返回一个接收M个参数的函数ret,同时还可以实现参数顺序调整等操作。
  • 它的原型有两种形式,如下:
    // simple(1)    
    template <class Fn,class... Args>
      /* unspecified */ bind (Fn&& fn,Args&&... args);
     
      with return type (2)    
    template <class Ret,Args&&... args);
  • bind返回一个基于fn的函数对象(function object),其参数被绑定到args上.
  • fn的参数要么是绑定到值,要么是绑定到placeholders(占位符,如_1,_2,…,_N)

    参数:

  • fn: 一个调用对象(可以是function objects, 函数指针或引用,成员函数指针或成员变量指针),它的参数将被args绑定args:
  • args:可变长参数,或者是具体的值,或者是占位符(placeholder). 注意:其长度必须与fn接收的参数个数一致
  • 返回值:

    令bind的返回值为ret:

    auto ret = std::bind(fn,args&&...)
     or
    auto ret = std::bind<Ret>(fn,args&&...)

    其返回值ret是一个未指定类型T的function object。

bind过程和std::placeholders的使用

  fn的参数要么是绑定到值,要么是绑定到placeholders

  1.完全绑定到值

void f(int n1,1)">int n2,1)">int n3)
{
    cout << n1 << " " << n2 << " << n3 << endl;
}
 
 f的三个参数,全部绑定到值,对empty_args的调用将不需要提供参数
auto empty_args = bind(f,1,1)">2,1)">3);     
empty_args();                1 2 3

  2.完全绑定到std::placeholders

 ret4() 
{ 
    cout << "ret4() called" << endl;
    return 4; 
}
 
 f参数都使用占位符绑定,需要提供至少三个参数
auto need_3args = bind(f,_1,_3);
need_3args(3);             1 2 3
need_3args(3,1)">4,1)">5);       1 2 3; 4和5被丢弃
need_3args();     ret4() called<cr> 1 2 3; 会调用ret4(),但是返回的4被丢弃

bind过程分析及调用传参控制

  在分析bind的绑定过程时,如何确定bind调用有没有错误,以及调用bind返回值ret的时候如何正确传参?比如,对下面的bind调用

int n3,1)">int n4,1)"> n5)
{
    cout << n1 << " << n3 << " << n4 << " << n5 << 如何分析bind的调用是否正确
auto mix1 = bind(f,1)">,_2);
auto mix2 = bind(f,_2);
auto mix3 = bind(f,_1);
auto mix4 = bind(f,_3,_1);
auto mix5 =);
auto mix6 = 如何填写mix的调用参数,他需要几个参数?
 mix1(...); 
 mix3(...); 
 mix4(...); 
 mix5(...); 

  

  1. bind(f,args…)的合法性分析

  设f需要的参数个数为N, bind(f…)中,提供的值的个数为V, 提供的占位符个数为s.对于合法的bind调用,必有 N == V + s. 如果V + S 超出N或者小于N,编译都会报错。所以上面的mix定义中,只有mix1,mix3,mix4,mix5是合法的。mix2中参数个数已经有6个,而f只需要5个。mix6的参数个数对,但是占位符太大了,在VC++(2013)编译器实现中最大为20。

  2. bind返回值ret的调用传参写法

  设bind(f,args…)中最大的占位符为_m. ( 如mix1,mix2,mix3中 M=2; mix4中 M=3; mix5中 M=1; mix6中M=100; )则:

  参数个数:

    ret的调用中至少要提供 M 个参数,因为_1~_M正是从ret(args…)参数列表中从左到右来按下标顺序绑定参数的,少于M个会报错,多于M个则被丢弃.

  参数顺序:

    ret(args…)中参数与placeholders: _1~_M的对应是很简单的,下标从1开始,依次对应。但是绑定到f的顺序是由bind(fn,args…)中placeholders的顺序决定的.如bind(f,_4,_5)就是从左到右的顺序把ret(args…)里的参数绑定到f;而bind(f,_1)则是按相反的顺序绑定。

  于是,对于上文中定义的合法mix的调用示例及输出可以是:

auto mix1 = bind(f,_1);
 
mix1(5);                  1 2 3 4 5; M = 2;
@H_87_14@mix1(4);                   no,参数太少了
mix3(5,1)">4);                  1 2 3 4 5; M = 2; 
mix4(0,1)">1);               1 2 3 4 5; M = 3; 第一个参数5给_1,第三个参数1给_3,第二个参数0被丢弃,因为bind中没用到_2.
mix5(5);                     5 5 5 5 5; M = 1;

  你可能注意到mix6中的_100,这样大的占位符是编译不过的。placeholders的最大值,在VC++上是20, 它的最大值依赖于具体的编译器实现,不需要纠结这个最大值. 一般不会使用很大的占位符,因为一旦你使用了一个占位符_100,然只用了一个,但是这就意味着调用者需要提供至少100个参数,你是想弄死谁?

std::bind 绑定普通函数、lambda表达式

include <functional>
 
 定义两个函数,乘、除法
double multiply(double d1,1)">double d2)
{
    return d1 * d2;
}
 
double divide( n)
{
    assert(n != 0);
    return d1 / n;
}
 
----------------------------- begin of new test -----------------------------
RUN_GTEST(FunctorTest,Bind,@);         google gtest 的简单封装,可以当做一个普通函数的开始!
 
using std::bind;                         for std::bind
using namespace std::placeholders;       for _1,_3 ...
 
 做“无用功”, same函数跟multiply一样,接收两个参数,并且顺序也是一致
auto same = bind(multiply,_2);
EXPECT_EQ(200.0,same(100));         
 
一个参数使用占位符,第二个单数绑定为2.0,从而返回一个函数仅需要传入一个参数,
 返回其2倍。
auto doublize = bind(multiply,1)">2.0);
EXPECT_EQ());
 
 全部使用参数绑定,ret_20不需要参数即可调用,返回20
auto ret_20 = bind(multiply,1)">1020.0);
 
 正常的相除函数,arg1/arg2
double d1 = divide(10,1)">25);
 
 通过bind,交换两个参数的顺序,revertDivide(arg1,arg2)将返回arg2/arg1
auto revertDivide = bind(divide,_1);
double d2 = revertDivide(1 / 5.0);
 
 bind的第二种形式,显式指定返回值类型为int
auto rounding = bind<int>(divide,_2);
auto i1 = rounding();
bool isSameType = is_same<int,decltype(i1)>::value;     i1 是int
EXPECT_TRUE(isSameTypE);
EXPECT_EQ();
 
 bind lambda函数
auto lambda_func = [](int X) -> int { return x; };
auto ret_100 = bind(lambda_func,ret_100);
 
 使用bind适配一个函数 
std::function<void(int)> func_with_1args;
func_with_1args = bind(multiply,_1);
 
END_TEST;

std::bind 绑定类成员函数、成员变量

  成员函数区别于普通函数一个特殊之处在于,其第一个参数必须是该类型的一个对象(或对象的指针或引用)

class Foo
{
public:
 
     n3)
    {
        cout << n1 <<  endl;
    }
 
    int a_ {  };
};
 
----------------------------- begin of new test -----------------------------
RUN_GTEST(FunctorTest,@);
 
Foo foo;
Foo& foo_ref= foo;
 
 成员函数原型:Foo::f(int n1,int n2,int n3);
 使用Foo::f,需要四个参数,1.Foo类型的对象(或其指针或引用); 2~4个参数则赋值给n1,n2,n3
 
 对返回值mfarg4的调用将需要4个参数: 按顺序绑定到占位符_1,_4.
auto mfarg4 = bind(&Foo::f,_4);
 使用对象本身调用
mfarg4(foo,1)">20,1)">30);         10 20 30; 
 使用对象指针调用
mfarg4(&foo,1)">30);        使用对象引用
mfarg4(foo_ref,1)">30);     10 20 30; 
 
 对返回值mfarg3的调用将需要三个参数:分别绑定到三个占位符,f的第四个参数将使用固定左值30.
auto mfarg3 = bind(&Foo::f,1)">30);
mfarg3(foo,20);             对返回值mfarg2调用将需要两个参数:分别绑定到两个占位符,f的后两个参数将使用固定左值20,30.
auto mfarg2 = bind(&Foo::f,1)">);
mfarg2(foo,1)">10);                 对返回值mfarg1的调用将仅需要一个参数foo对象,绑定到唯一的占位符,后单个参数将使用bind中的固定左值10,20,30.
auto mfarg1 = bind(&Foo::f,1)">);
mfarg1(foo);                     10 20 30;
 
 完全使用左值绑定,返回值mfarg0将不需要使用参数进行调用.
auto mfarg0 = bind(&Foo::f,foo,1)">);
mfarg0();                        10 20 30
 
 使用对象指针或引用也可以
auto mfarg01 = bind(&Foo::f,&foo,1)">);
mfarg01();                       10 20 30
auto mfarg02 = bind(&Foo::f,foo_ref,1)">);
mfarg02();                      一个成员函数赋值给一个std::function
std::function< normal_func;
normal_func = bind(&);
normal_func(------------------------------ 绑定成员变量a_ ---------------------------
 
 bind成员变量,其第一个参数必须是该类型的一个对象(或对象或引用),指针不行!!
 
auto bind_mv = bind(&Foo::a_,_1);
cout << bind_mv(foo);            100
cout << bind_mv(foo_ref);        100
cout << bind_mv(&foo);         error,成员变量不能用对象指针来绑定
 
END_TEST;

std::bind 绑定模板函数

/ 定义一个函数模板,返回两数之和,返回值是两数之和的类型。使用了c++11中的Trailing  types特性.
template <typename T1,typename T2>
auto add(const T1 & t1,1)">const T2& t2) -> decltype(t1 + t2)
{
    return t1 + t2;
}
 
 work with template function.
auto addby2 = bind(add<double,1)">double>,1)">);
cout << addby2(10.2);                              12.2

嵌套std::bind共享std::placeholder.

void print( 定义一个辅助函数
auto addby1 = [] ( 
{ 
    cout << addby1() calledreturn (x+1); 
};
 
 嵌套的bind在使用外层bind()中的placeholders
auto nested_f = bind(print,bind(addby1,_1),_2); 
 
nested_f(3);                  addby1() called<cr> 1 2 3

reference_wrapper<T>类型,实现绑定引用

nt x{};
 
 第二个参数使用引用X,第三个参数使用值x
auto bind_ref = bind(print,std::cref(X),X);  
bind_ref();                 1 10 10; 
 
x = ;
 
bind_ref();                 1 100 10; 第二个参数跟着x变化了,第三个则没变

bind与标准库协同工作

redefinedFunctors,@);
 
 all predefined functors:
 negate,plus,minus,multiplies,divides,modulus,equal_to, not_equal_to,less,greater,less_equal,greater_equal,1)"> logical_not,logical_and,logical_or,bit_and,bit_or,bit_xor
 
auto tenTimes = bind(multiplies<int>(),1)">100,tenTimes());
EXPECT_EQ(200,1)">20300,1)">));
 
vector<int> v{ 6,1)">7,1)">8 };
 nested bind. output v[i] if 10*v[i] > 50.
copy_if(v.begin(),v.end(),ostream_iterator<int>(cout,1)">"),bind(greater<(),bind(multiplies<50));                                6,7,8,
cr;
 
END_TEST;

bind与智能指针

struct Temp 
{
    Temp(int i=) : i_(i) {}
    void print() { pln(i_); }
     i_;
};
 
vector<shared_ptr<Temp>> vs =
{
    shared_ptr<Temp>(new Temp()),shared_ptr<Temp>(.begin(),vs.end(),bind(&Temp::print,_1));   1<cr>2<cr>3<cr>
 
bind(&Temp::print,vs[0])();         1
bind(&Temp::print,1)">1])();         2
bind(&Temp::print,1)">2])();         3
 
END_TEST;

     bind中的参数是被copy或者是被move到目标函数的,除非显示指定按引用传递,用std::ref 或者std::cref来包裹参数,否则是不会按引用来传递的。这意味着你要意识到,在bind一个大的对象作为参数的时候可能存在的拷贝开销,应该尽量用引用**

  同时,因为move会改变对象的状态,因此,所以当你在bind参数列表里重用placeholders的时候,要虑到参数已经被moved掉的情况,标准建议:只有在参数是左值或者不可移动的右值的时候,重用placeholders才有意义。

 

大佬总结

以上是大佬教程为你收集整理的C++11 std::bind全部内容,希望文章能够帮你解决C++11 std::bind所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。
标签: