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);});
}