#仿函数
#Part1: 仿函数是什么?
众所周知,STL里有一个玩意儿叫 greater
。
他最大的用处在 sort
里,比如说
sort(vec.begin(),vec.end(),greater<int>());
那么问题来了:那个()
是什么?我们自定义cmp函数时不都是用
sort(vec.begin(),vec.end(),cmp);
的吗?
有人说,()
表示这是一个函数。恭喜,答对了一半,这是仿函数。
但是请注意,说仿函数接近函数是七窍通了六窍。仿函数,其实是类,不是函数!
那么既然是类,那么()
的作用就说得通了:这是类的初始化,初始化完就和cmp
等价。
在正常的地方,调用仿函数的格式为func(/*类初始化*/)(参数)
,例如greater<int>()(1,2)
#Part2: 怎么写仿函数?
可一个类,怎么能当函数耍呢?
众所周知,c++里有伟大的重载运算符。
回忆一下调用函数的过程:
其中最核心的就是一个()
。
俗话说,万物可重载。既然要用()
,那就把它重载了!
还记得重载运算符的格式吗?
<返回类型> operator<运算符> (<类型> a, <类型> b)
{
return something;
}
那么,我们也照样来一遍:把运算符换成()
,把类型换成想要的:
type operator() (type a, type b, type c)
{
return something;
}
没错,这样就能当函数耍了!
举个栗子:
#include<bits/stdc++.h>
using namespace std;
class square
{
public:
int operator() (int x)
{
return x*x;
}
};
int main(){
int x;
cin>>x;
cout<<square()(x);
return 0;
}
#输入:
3
#输出:
9
#Part3: 仿函数有什么用?
STL里面有一个东西叫做count_if
。
我们用正常的方式自定义函数来统计一串数中大于5的数的个数:
bool check(int x)
{
return x>5;
}
然后只要count_if(vec.begin(), vec.end(), check)
就行了。
但是如果要统计大于n的数怎么办?如果check函数用两个参数STL是不接受的。
于是我们就想到了仿函数。
class check
{
public:
int x;
check(int _x)
{
x=_x;
}
bool operator() (int _toCheck)
{
return _toCheck>x;
}
};
之后在主函数里count_if(vec.begin(), vec.end(), check(n))
就搞定。
另外,STL中自定义函数指针是不能用模版函数的,所以需要模版函数时用仿函数就很好。
例如:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
template<typename tp> class mygreater
{
public:
bool operator() (tp & a, tp & b);
};
template<typename tp>
bool mygreater<tp>::operator() (tp & a, tp & b)
{
return a>b;
}
int a[10000],n;
int main(){
cin>>n;
for(int i=1; i<=n; i++) scanf("%d",&a[i]);
sort(a+1,a+1+n,mygreater<int>());
for(int i=1; i<=n; i++) cout<<a[i]<<' ';
return 0;
}
#Part4: 实际应用
学生去重
我们可以使用仿函数来作为比较函数。
struct stu
{
string name;
int a,b,c,d;
}student[100001];
class my_less
{
public:
bool operator() (const stu & a, const stu & b)
{
return a.name<b.name;
}
};
template<int x> class my_equal
{
public:
bool operator() (const stu & a, const stu & b)
{
return a.name==b.name && abs(a.a+a.b+a.c+a.d-b.a-b.b-b.c-b.d)<=x;
}
};
主函数部分:
stable_sort(student,student+n,my_less());
int idx=unique(student,student+n,my_equal<3>())-student;
for(int i=0; i<idx; i++)
{
cout<<student[i].name<<' '<<student[i].a<<' '<<student[i].b<<' '<<student[i].c<<' '<<student[i].d<<endl;
}
#Part5: 配接
话说lls整天没事干,于是把统计学生的题目改了一下:统计分数之和大于等于m的学生数量,怎么办?
我们当然可以像统计大于n的数的个数一样,写成check(m)
的形式,但如果我们不想这么干呢?
第一件想到的事情就是用仿函数greater<stu>
但是count_if
只接受带一个参数的函数,怎么办?
那就把第二个参数绑定成一个特定的值呗:
bool operator> (stu a, stu b)
{
return a.a+a.b+a.c+a.d>b.a+b.b+b.c+b.d;
}
int main(){
cin>>n>>m;
stu cmp=(stu){"nil",m,0,0,0};
for(int i=0; i<n; i++)
{
string x;
int a,b,c,d;
cin>>x>>a>>b>>c>>d;
student[i]=(stu){x,a,b,c,d};
}
cout<<count_if(student,student+n,bind2nd(greater<stu>(),cmp));
return 0;
}
类似的函数有四个:(仿函数名op
,固定值value
,数组中的值x
)
bind1st(op(),value
: 相当于调用op()(value,x)
bind2nd(op(),value
: 相当于调用op()(x,value)
not1(op())
: 相当于调用!op()(x)
not2(op())
: 相当于调用!op()(x_1, x_2)
栗子:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,m;
struct stu
{
string name;
int a,b,c,d;
}student[100001];
bool operator> (stu a, stu b)
{
return a.a+a.b+a.c+a.d>b.a+b.b+b.c+b.d;
}
int main(){
cin>>n;
stu cmp=(stu){"nil",m,0,0,0};
for(int i=0; i<n; i++)
{
string x;
int a,b,c,d;
cin>>x>>a>>b>>c>>d;
student[i]=(stu){x,a,b,c,d};
}
sort(student,student+n,not2(greater<stu>()));
for(int i=0; i<n; i++)
{
cout<<student[i].name<<' '<<student[i].a<<' '<<student[i].b<<' '<<student[i].c<<' '<<student[i].d<<endl;
}
return 0;
}
#Part6: 参考文献
CSDN