您的位置:首页 > 编程语言 > C语言/C++

C++如何写adaptable仿函数

2017-12-20 19:47 106 查看
关于仿函数很多博客上都有,形式很简单,用途也很多。

一句话总结:重载了()运算符的类可以称之为仿函数functor

现在写一个“比较”

class MyLess{
public:
bool operator()(int a, int b){
return a < b;
}
};


大多数人会这样写,其实这样写没有错,而且基本所有算法都可以通过

比如:

class MyLess{
public:
bool operator()(int a, int b){
return a < b;
}
};
class Less10{
public:
bool operator()(int a){
return a < 10 ? true : false;
}
};
int main() {
vector<int> v{1, 40, 5, 60, 5, 6};
cout<<count_if(v.begin(), v.end(), Less10())<<endl;
sort(v.begin(), v.end(), MyLess());
for(auto i : v)
cout<<i<<" ";
cout<<endl;
}


上面两个仿函数MyLess、Less10。两个算法count_if、sort。

这样的仿函数基本上可以用stl中所有的算法。

看看STL中less怎么写的吧

template <class T>
struct less : public binary_function<T, T, bool> {
bool operator()(const T& x, const T& y) const { return x < y; }
};


除了模板,还有就是less继承了个binary_function,这个是什么鬼?

为什么要继承它?继承它有啥用?

看一个例子:

class MyLess{
public:
bool operator()(int a, int b){
return a < b;
}
};
int main() {
vector<int> v{1, 40, 5, 60, 5, 6};
cout<<count_if(v.begin(), v.end(), bind2nd(less<int>(), 50))<<endl;
}


bind2nd这个叫绑定器,可以绑定一个仿函数的第二个参数。

为啥要使用绑定器?看看第段代码,有没有发现我统计数组中小于10的元素个数太单一了?一点都没有可扩展性,当我们要小于50的时候又得再写一个仿函数,太累了。所以绑定器产生了,现在C++11的绑定都是用bind这个函数(这个函数太厉害了,比bind1nd,bind2nd强大的多,有机会可以去了解下)

回到正题,如果我们把less()换成我们自己写的仿函数,编译就有问题,为啥呢?抛开模板,就是没有继承那个什么鬼了。

看看那个鬼张啥样吧

template <class Arg1, class Arg2, class Result>
struct binary_function {
typedef Arg1 first_argument_type;
typedef Arg2 second_argument_type;
typedef Result result_type;
};


啥?一个成员都木有啊。

其实看STL最大的收获是懂得了typedef的使用,别看简单的一个typedef,几乎在stl中无处不在,绑定器,适配器,萃取器等等,都和typedef密不可分,但是我们正真写程序用typedef的又有几个。

回到正题,我们在看看bind2nd

template <class Operation, class T>
inline binder2nd<Operation> bind2nd(const Operation& op, const T& x) {
typedef typename Operation::second_argument_type arg2_type;
return binder2nd<Operation>(op, arg2_type(x));
}


原来bind2nd要用typedef得到第二参数的类型啊,有啥用?为了检查第二参数是否类型正确,如果转换不了那就直接出错

so,结果就出来了,为啥要继承那个鬼。

再继续看看binder2nd吧

template <class Operation>
class binder2nd
: public unary_function<typename Operation::first_argument_type,
typename Operation::result_type> {
protected:
Operation op;
typename Operation::second_argument_type value;
public:
binder2nd(const Operation& x,
const typename Operation::second_argument_type& y)
: op(x), value(y) {}
result_type operator()(const argument_type& x) const {
return op(x, value);
}
};


这里也需要知道参数类型和返回类型,而且binder2nd本身也是一个仿函数,而且继承了unary_function类

template <class Arg, class Result>
struct unary_function {
typedef Arg argument_type;
typedef Result result_type;
};


到此为止我们可以总结了

仿函数如果要融入STL那么需要继承一个类

一个参数用unary_function

两个参数用binary_function

注意:为啥binary_function有两个参数还是继承unary_function呢?

因为binary_function需要别人推导的只有第一参数而已,第二参数类型已经确定,在本类中已经typedef了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息