bind绑定器和function函数对象

bind绑定器和function函数对象

bind1st和bind2nd什么时候使用

在c++ STL中提供了泛型算法

#include <iostream>
#include "vector"
#include <functional>  //包括了函数对象
#include "algorithm"   //包括了泛型算法
#include "ctime"
using namespace std;

template<typename Container>
void showContainer(Container &con)
{
    typename Container::iterator it = con.begin();
    for(it;it!=con.end();it++)
    {
        cout<<*it<<" ";
    }
    cout<<endl;
}

int main()
{
    vector<int >vec;
    srand(time(nullptr));
    for(int i=0;i<20;i++)
    {
        vec.push_back(rand()%100 + 1);
    }
    sort(vec.begin(),vec.end());  //默认从小到大,默认是less类的operator()的重载函数
    showContainer(vec);
    sort(vec.begin(),vec.end(),greater<int>());  //默认从大到小,主动调用共greader类的operator()的重载
    showContainer(vec);
}

这里的greader< int > 和 less< int >都是二元函数(有两个参数)

greater类的源码
template<typename _Tp>
    struct greater : public binary_function<_Tp, _Tp, bool>
    {
      _GLIBCXX14_CONSTEXPR
      bool
      operator()(const _Tp& __x, const _Tp& __y) const
      { return __x > __y; }
    };

但是,现在有这样一个需求,要获取满足小于70的数
greader< int > 和 less< int>都是vector 内元素互相比较,所以只靠greader< int>和less< int>是难以实现的。但是如果可以固定其中的一个参数为70,这样就实现了用vector中的元素与70相比较。实现这一要求就要用到 bind1st 和 bind2nd 绑定

bind1st()和bind2nd()都是把二元函数转化为一元函数,方法是绑定其中一个参数。
bind1st()是绑定第一个参数。
bind2nd()是绑定第二个参数。 

使用:

int main()
{
    vector<int >vec;
    srand(time(nullptr));
    for(int i=0;i<20;i++)
    {
        vec.push_back(rand()%100 + 1);
    }
    sort(vec.begin(),vec.end());  //默认从小到大
    showContainer(vec);
    auto it = find_if(vec.begin(),vec.end(),bind2nd(greater<int>(),70));
    //返回一个迭代器
    cout<<*it;
}

bind1st和bind2nd的底层原理

template<typename Iterator,typename Compare>
Iterator my_find_if(Iterator first,Iterator last,Compare comp)
{
    for(;first != last;++first)
    {
        if(( comp(*first) ) )   //调用 Compare的() 重载函数
            return first;
    }
    return last;
}

template<typename Container>
void showContainer(Container &con)
{
    typename Container::iterator it = con.begin();
    for(it;it!=con.end();it++)
    {
        cout<<*it<<" ";
    }
    cout<<endl;
}

template<typename Compare,typename T>
class _mybind1st
{
public:
    _mybind1st(Compare comp,T val)
        :_comp(comp),_val(val)
    {}
    
    bool operator()(const T &second)
    {
        return _comp(_val,second);
    }
private:
    Compare _comp;
    T _val;
};

template<typename Compare, typename T>
_mybind1st<Compare,T> mybind1st(Compare comp, const T &val)
{
    // 直接使用函数模板,好处是,可以进行类型的推演
    return _mybind1st<Compare, T>(comp, val);
}

template<typename Compare,typename T>
class _mybind2nd
{
public:
    _mybind2nd(Compare comp,T val)
        :_comp(comp),_val(val)
    {
    }
    bool operator()(T first)
    {
        return _comp(first,_val);
    }
private:
    Compare _comp;
    T _val;
};


template<typename Compare,typename T>
_mybind2nd<Compare,T> mybind2nd(Compare comp,T val)
{
    return _mybind2nd<Compare,T>(comp,val);   //返回一个类对象
}

int main()
{
    vector<int> vec;
    srand(time(nullptr));
    for (int i = 0; i < 20 ; ++i)
    {
        vec.push_back(rand() % 100 + 1);
    }

    showContainer(vec);
    sort(vec.begin(), vec.end()); // 默认小到大排序
    showContainer(vec);

    // greater 二元函数对象
    sort(vec.begin(), vec.end(), greater<int>()); // 大到小排序
    showContainer(vec);

    /*
    把70按顺序插入到vec容器当中   找第一个小于70的数字
    operator()(const T &val)
    greater   a > b
    less      a < b
    绑定器 + 二元函数对象 =》 一元函数对象
    bind1st: + greater bool operator()(70, const _Ty& _Right)
    bind2nd: + less bool operator()(const _Ty& _Left, 70)
    */
    
    auto it1 = my_find_if(vec.begin(), vec.end(),
                          mybind1st(greater<int>(), 70));
    //auto it1 = my_find_if(vec.begin(), vec.end(),
//    bind2nd(less<int>(), 70));
    if (it1 != vec.end())
    {
        vec.insert(it1, 70);
    }
    showContainer(vec);
    return 0;
}

function函数对象类型的应用实例

c++11提供了绑定器(bind)和函数对象(function)

function解决了 绑定器,函数对象,lambda表达式,他们只能使用在一条语句中,也就是说function对象可以接受绑定器,函数对象,lambda表达式,并使用function对象如同使用他们一样

function类的定义

template<typename _Res, typename... _ArgTypes>
    class function<_Res(_ArgTypes...)>    //返回值类型(参数类型),这是函数类型

使用:

void hello1()
{
    cout<<"hello world"<<endl;
}

int sum(int a,int b)
{
    int ret = a+b;
    cout<<ret<<endl;
    return ret;
}
void hello2(string str)
{
    cout<<str<<endl;
}

int main()
{
    function<void(void)> func = hello1;        //普通函数的绑定
    func();

    function<void(string)> func2 = hello2;     //普通函数的绑定
    func2("aaa");

    function<int(int,int)> func3(sum);
    func3(1,2);                        

    function<int(int)>func4 = bind(sum,1,std::placeholders::_1);  //绑定bind绑定后的函数对象
    func4(1);
    
    return 0;
}
class hello
{
public:
    void sayCount(int val)
    {
        cout<<"sayHello"<<val<<endl;
    }
};

int main()
{
    function<int(int,int)> func1 = [](int a,int b){return a+b;};
    cout<<func1(1,2)<<endl;                                 //对lambda表达式的绑定

    function<void(hello*,int)> func2 = &hello::sayCount;    //对类成员函数的绑定
    hello h1;
    func2(&h1,2);
    return 0;
}

模板的完全特例化与部分特例化

只有类模板才支持部分特例化,函数模板特例化时必须为原模板中每个模板参数都提供实参

/*
 * 完全特例化是 template<>
 * 部分特例化是 template<typenaem 参数,typename 参数>*
 *  指针类型的部分特例化 template<typename Ty> class Vector<Ty*> {}
 *  函数类型的特例化 template<typename R,typrname A1,typename A2> class Vector<R(*)(A1,A2)>{}
 *  */

template<typename T>
bool compare(T a,T b)
{
    cout<<"bool compare(T a,T b)"<<endl;
    return a>b;
}

template<>         //函数模板的特例化
bool compare<const char*>(const char* a,const char* b)
{
    cout<<"bool compare(const char* a,const char* b)"<<endl;
    return strcmp(a,b);
}

template<typename T>
class Vector<T>                 //普通模板
{
public:
        Vector() {cout<<"call Vector init"<<endl;}
};


template<>
class Vector<const char*>    //完全特例化
{
public:
    Vector<const char*>(){ cout << "Vector<const char*> " << endl; }
};


template<typename Ty>
class Vector<Ty*>              //部分特例化,传入指针类型。匹配这个
{                              //若传入int* 那么Ty将推到为 int
public:
    Vector() {cout<<"Vector<Ty*>"<<endl;}
};

template<>
class Vector<int(*)(int,int)>    //对函数指针类型提供的完全特例化
{
public:
    Vector<int(*)(int,int)>() {cout<<"class Vector<int(*)(int,int)> 完全特例化"<<endl;}
};

template<typename R,typename A1,typename A2> // 对函数指针类型提供的部分特例化
class Vector<R(*)(A1,A2)>
{
public:
    Vector<R(*)(A1,A2)>(){cout<<"class Vector<R(A1,A2)>"<<endl;}
};

template<typename R,typename A1,typename A2>  //对函数类型提供的部分特例化
class Vector<R(A1,A2)>
{
public:
    Vector(){cout<<"class Vector<R(A1,A2)>"<<endl;}
};

模板的实参类型推演

先来看一下函数类型与函数指针类型

template<typename T>
void func(T* a)
{
    cout<< typeid(T).name()<<endl;
}
int sum(int a,int b)
{
    return a+b;
}
int main()
{
    func(sum);
}
输出:int __cdecl(int,int)      
//这里推演出 T 为函数类型
//__cdecl 是规定了参数压栈的顺序

template<typename T>
void func(T a)
{
    cout<< typeid(T).name()<<endl;
}
int sum(int a,int b)
{
    return a+b;
}
int main()
{
    func(sum);
}
输出:int (__cdecl*)(int,int)
//这里推演出 T 为函数指针类型

这样模板类型参数 T 就是一个函数指针类型或者是函数类型,可是,函数类型有些大了,想要将他细分为 返回值类型,和参数类型

对于普通函数

template<typename R,typename A1,typename A2>
void func(R(*)(A1,A2))
{
    cout<< typeid(R).name()<<endl;
    cout<< typeid(A1).name()<<endl;
    cout<< typeid(A2).name()<<endl;
}

int sum(int a,int b)
{
    return a+b;
}

int main()
{
    func(&sum);   
    //也可以是func(sum) func(*sum) c++为了兼容c语言,对于普通函数,没有区分函数名 &函数名 *函数名
}
输出:
int
int
int

对于类函数

首先看下类函数指针的表示法

template<typename T>
void func(T a)
{
    cout<< typeid(T).name()<<endl;
}
class Test
{
public:
    int sum(int a,int b){return a+b;}
};

int main()
{
    func(&Test::sum);
}
输出:int (__thiscall Test::*)(int,int)  //int返回值类型 Test类类型 (int,int)参数类型

对类型进行拆分:

template<typename R,typename T,typename A1,typename A2>
void func(R(T::*)(A1,A2))
{
    cout<< typeid(R).name()<<endl;
    cout<< typeid(T).name()<<endl;
    cout<< typeid(A1).name()<<endl;
    cout<< typeid(A2).name()<<endl;
}

class Test
{
public:
    int sum(int a,int b){return a+b;}
};

int main()
{
    func(&Test::sum); 
    //传入一个类成员函数,必须加 &,因为类是c++相对c语言所特有的
    //没有必要再按照c语言的那一套来
}
输出:
int
class Test   //推导出 T是一个类类型,所以是可以通过 T来定义对象的
int
int

function的实现原理

template<typename Fty>
class myfunction{};

template<typename R,typename ...A1>     //对于普通函数的部分特例化
class myfunction<R(A1...)>
{
public:
    /*
     * 为什么不使用函数类型 R(A1...)
     * 因为函数类型之间无法赋值
     *
     * int sum(int a,int b)
        {return a+b;}

        int main()
       {
            int (p)(int,int) = sum;  //报错无法初始化,所以更别提赋值了
       }
     * */

    using PFUNC = R(*)(A1...);

    myfunction(PFUNC func)
    {
        _pfunc = func;
    }

    R operator()(A1 ...argv)
    {
        return _pfunc(argv...);
    }
private:
    PFUNC _pfunc;
};

int sum(int a,int b)
{
    return a+b;
}

int main()
{
   myfunction<int(int,int)> p = sum;
   p(1,2);
   return 0;
}

bind的使用

bind是函数模板,所以可以进行参数的类型推演,也就不需要传递参数,返回一个函数对象,这个对象所在的类重载了 ()

int sum(int a,int b)
{
    return a+b;
}

class Test
{
public:
    int sum(int a,int b)
    {
        return a+b;
    }

};

    int main()
    {
        cout<<bind(sum,1,2)()<<endl;
        cout<<bind(&Test::sum,Test(),1,placeholders::_1)(1);
	}

lambda的原理

lambda表达式语法:

[ 捕获变量 ] ( 形参列表 ) -> 返回类型 {操作代码}

[捕获捕获外部变量] :

[ ] :表示不捕获任何外部变量

[=] :以传值的方式捕获外部变量

[&] :以传引用的方式捕获外部变量

[this] :捕获外部this指针

[=,&a] :以传值的方式捕获外部所有变量,但是a变量以传引用的方式捕获

[a,b] :以值传递的方式捕获变量 a , b

[a,&b] : a以值传递捕获,b以引用传递的方式捕获

C++11 引入了 Lambda 表达式,程序员可以凭此创建匿名函数。在 Lambda 表达式的设计中,捕获变量有几种方式;其中按值捕获(Caputre by Value)的方式不允许程序员在 Lambda 函数的函数体中修改捕获的变量。而以 mutable 修饰 Lambda 函数,则可以打破这种限制。

按值传递要加mutable的原因
int main()
{
    int a = 10;
    int b = 20;
    auto func3 = [=]()->void
            {
                int tem = a;
                a  = b;
                b = tem;
            };
    func3();
}
报错:
    Cannot assign to a variable captured by copy in a non-mutable lambda
    
修改:
int main()
{
    int a = 10;
    int b = 20;
    auto func3 = [=](int a,int b) mutable -> void
            {
                int tem = a;
                a  = b;
                b = tem;
            };
    func3(10,20);
}    

将这个lambda函数对象抽象出来

class TestLambda03
{
public:
	TestLambda03(){}     //相当于almbda中的 []
    void operator()(int a,int b) const   //相当于lambda中 ()
    {
    	 int tem = a;
    	 a  = b;
         b = tem;
    }
private:
	mutable int ma;      //相当与()后的mutable
	mutable int mb;
};
按引用传递不需要加mutable的原因
class A
{
public:
    A(int& a_,int& b_,int& c_)
        :a(a_)
        ,b(b_)
        ,c(c_)
    {}
    void operator() const
    {
     	int tem = a;
     	a = b;
     	b =tem;
		//由于const修饰的是a b c,而a b c的本质是4个字节的地址
		//这里的赋值修改的是引用的内存,并没有修改引用变量本身
    }
private:
    int& a;
    int& b;
    int& c;
};

lambda的应用

  • 自定义删除器
int main()
{
    unique_ptr<FILE,function<void(FILE*)>> pf(fopen("a.txt","w")
                                              ,[](FILE* fp){ fclose(fp);});
}
posted @ 2023-01-23 13:06  satellite2002  阅读(36)  评论(0编辑  收藏  举报