技术在于交流、沟通,本文为博主原创文章转载请注明出处并保持作品的完整性

概要:

1.仿函数

2.bind2nd()

3.not1()

4.bind()


仿函数的实现:声明一个类,重载它的operator call ("()"操作符)

template<typename T>
class lineFeed
{
public:
void operator()(const T &x)
{
cout<<x<<endl;
}
};

仿函数只为算法服务,但是像上面这种声明方式,虽然在有些时候可以使用,但是却不能融入STL,因为有可能在"仿函数适配器"上出现编译错误,下面我们来看看STL中的仿函数.

仿函数的使用可以看一下我的上一节博客,虽然都没有继承自 binary_function<T,T,bool>,unary_function<T,bool>...

STL中的仿函数大致可以分为三个类

1.算数类

  template<typename _Tp>
struct plus : public binary_function<_Tp, _Tp, _Tp>
{
_Tp
operator()(const _Tp& __x, const _Tp& __y) const
{ return __x + __y; }
}; template<typename _Tp>
struct minus : public binary_function<_Tp, _Tp, _Tp>
{
_Tp
operator()(const _Tp& __x, const _Tp& __y) const
{ return __x - __y; }
};
...

2.逻辑运算类

  template<typename _Tp>
struct logical_and : public binary_function<_Tp, _Tp, bool>
{
bool
operator()(const _Tp& __x, const _Tp& __y) const
{ return __x && __y; }
}; template<typename _Tp>
struct logical_or : public binary_function<_Tp, _Tp, bool>
{
bool
operator()(const _Tp& __x, const _Tp& __y) const
{ return __x || __y; }
}; template<typename _Tp>
struct logical_not : public unary_function<_Tp, bool>
{
bool
operator()(const _Tp& __x) const
{ return !__x; }
}; ...

3.相对关系类

  template<typename _Tp>
struct equal_to : public binary_function<_Tp, _Tp, bool>
{
bool
operator()(const _Tp& __x, const _Tp& __y) const
{ return __x == __y; }
}; template<typename _Tp>
struct not_equal_to : public binary_function<_Tp, _Tp, bool>
{
bool
operator()(const _Tp& __x, const _Tp& __y) const
{ return __x != __y; }
};
...

我们会看到所有的STL仿函数都需要继承binary_function<T, T, bool>(两个参数)或者unary_function<T, bool>(一个参数),只有继承自这两种父类,你声明的仿函数才可以融入STL

上面提及到仿函数适配器, 如果不继承上面这两种类, 在执行到某个阶段会报错.

下面是binary_function<>与unary_function<>的源码

binary_function<T, T, bool>,发现binary_function<>

  template<typename _Arg1, typename _Arg2, typename _Result>
struct binary_function
{
/// @c first_argument_type is the type of the first argument
typedef _Arg1 first_argument_type; /// @c second_argument_type is the type of the second argument
typedef _Arg2 second_argument_type; /// @c result_type is the return type
typedef _Result result_type;
};

unary_function<T, bool>

  template<typename _Arg, typename _Result>
struct unary_function
{
/// @c argument_type is the type of the argument
typedef _Arg argument_type; /// @c result_type is the return type
typedef _Result result_type;
};

那么为什么没有继承自这两个类,会报错呢?我们看上面的源码,他们的内部都有typedef,这几个typedef是为了提取算法传进变量,以供仿函数适配器使用,如果你没有继承这两种类,那么在算法调用仿函数时,仿函数适配器提取不出这几个变量,那么就会报错.

写仿函数,一定要继承上面这两个类.


仿函数适配器

下面介绍几个仿函数用的适配器函数

1.bind2nd().它的作用是绑定仿函数的第二参数

它本身就是一个仿函数的适配器,它的作用是为我简化binder2nd(),因为binder2nd()的参数类型比较复杂,我们在调用的时候回很不方便.

先看一个测试函数

namespace wzj007 {
void test_Function()
{
vector<int> myVector = {,,,,,};
cout << count_if(myVector.begin(), myVector.end(),bind2nd(less<int>(),));//返回vector中value小于40的个数
}
}

现在解释一下bind2nd()函数的作用: 它将40 这个变量绑定在less()函数的第二参数上,less函数返回第一参数是否小于第二参数,那么绑定后的less()函数就应该返回 传入的参数是否小于40

小于40的value有10,20.那么输出的结果应该是2.那么具体是怎么绑定上的呢

我们先看一下less()的源码

template <class T> struct less {
bool operator() (const T& x, const T& y) const {return x<y;}
typedef T first_argument_type;
typedef T second_argument_type;
typedef bool result_type;
};

他也是一个仿函数,但是却发现它没有继承自binary_function<T, T, bool>,因为他自己声明了那三种typedef,他需要两个两个参数,x和y,返回 x是否小于y.

那么 bind2nd(less<int>(),40) 他的含义就应该是 比较传进参数x,是否小于40(y);

下面我们来看一下是怎么绑定上的

这是bind2nd()的源代码

  template<typename _Operation, typename _Tp>
inline binder2nd<_Operation>
bind2nd(const _Operation& __fn, const _Tp& __x)
{
typedef typename _Operation::second_argument_type _Arg2_type;//这有一个typedef 它提取出operation的第二参数,同时可以检测第二参数类型
return binder2nd<_Operation>(__fn, _Arg2_type(__x));
}

这个binder2nd()的源码,binder2nd()是一个仿函数,他的作用是绑定仿函数的的第二参数

template<typename _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) { }//构造函数(在bind2nd()函数中调用),初始化自身成员变量 op,value typename _Operation::result_type
operator()(const typename _Operation::first_argument_type& __x) const
{ return op(__x, value); }//重载operator() 在函数count_if()中被调用 // _GLIBCXX_RESOLVE_LIB_DEFECTS
// 109. Missing binders for non-const sequence elements
typename _Operation::result_type
operator()(typename _Operation::first_argument_type& __x) const
{ return op(__x, value); }//非const
} _GLIBCXX_DEPRECATED;

这是count_if()的源码

template <class Inputerator, class Outputerator, class Predicate>
typename iterator_traits<Inputerator>::difference_type;
count_if(Inputerator first, Inputerator last, Predicate pred)
{
typename iterator_traits<Inputerator>::difference_type;
for( ; first != last; ++first)
if(pred(*first)) //这个地方会调用函数pred(*first), 重上面我们可以看到 pred绑定的函数是binder2nd()中的 operator()函数,那么此时的pred就应该是less函数
++n;
return n;
}

现在band1st()的含义我们应该可以猜到了吧.


not1()将函数的返回值取否

他也是一种仿函数适配器,简化我们调用unary_negate()

    void test_Function_bind2nd()
{
vector<int> myVector = {,,,,,};
cout << count_if(myVector.begin(), myVector.end(),not1(bind2nd(less<int>(),)));//
}

调用not1()之前返回的是小于40的value有几个,那么取否后就应该是不小于40的value有几个: 4个(40,70,90,100)

看一下是怎么关联上的

not1()源码

  template<typename _Predicate>
inline unary_negate<_Predicate>
not1(const _Predicate& __pred)
{ return unary_negate<_Predicate>(__pred); }//调用unary_negate()的构造函数

unary_negate()源码

  template<typename _Predicate>
class unary_negate
: public unary_function<typename _Predicate::argument_type, bool>
{
protected:
_Predicate _M_pred; public:
explicit
unary_negate(const _Predicate& __x) : _M_pred(__x) { }//not1()调用构造函数初始化成员变量 bool
operator()(const typename _Predicate::argument_type& __x) const
{ return !_M_pred(__x); }//取反 count_if()调用其operator()
};

bind() C++11的绑定函数

这些函数是C++11的一些变更函数,原来的函数仍然可以使用,从上面可以看出 bind2nd改名为bind();

那么bind()具体怎么使用呢

这份代码是我在http://www.cplusplus.com上拷下来的

namespace wzj008 {

    // a function: (also works with function object: std::divides<double> my_divide;)
double my_divide (double x, double y) {return x/y;} struct MyPair {
double a,b;
double multiply() {return a*b;}
};
void test_Function_bind()
{
using namespace std::placeholders; // 使用placehloders _1(第一参数占位符), _2(第二参数占位符) ,_3(第三参数占位符)... // binding functions:
auto fn_five = std::bind (my_divide,,); // returns 10/2 将10 绑定在my_divide()函数的第一参数上, 2绑定在my_divide()函数的第二参数上 10/2 = 5
std::cout << fn_five() << '\n'; // auto fn_half = std::bind (my_divide,_1,); // returns x/2 使用占位符_1 即将my_divide()函数的第一参数绑定为传入参数, 2绑定为my_divide()函数的第二参数
std::cout << fn_half() << '\n'; // 5         10/2 = 5 auto fn_invert = std::bind (my_divide,_2,_1); // returns y/x 使用占位符_2 即_2为函数my_divide()的第二参数, _1为my_divide()的第一参数
std::cout << fn_invert(,) << '\n'; // 0.2       10(_2)为第二参数 2(_1)第一参数 2/10 = 0.2 auto fn_rounding = std::bind<int> (my_divide,_1,_2); // returns int(x/y) 将fn_rounding()绑定为my_divide()函数
std::cout << fn_rounding(,) << '\n'; // MyPair ten_two {,};                     //绑定成员变量 // binding members:
auto bound_member_fn = std::bind (&MyPair::multiply,_1); // returns x.multiply()
std::cout << bound_member_fn(ten_two) << '\n'; // 20 将传进参数ten_two{10,2} 的10和2 分别赋给 a和b(a = 10, b = 2) 10 * 2 = 20 auto bound_member_data = std::bind (&MyPair::a,ten_two); // returns ten_two.a 给a赋值 a = 10;
std::cout << bound_member_data() << '\n'; //
}
}

参考侯捷<<STL源码剖析>>

STL标准库-仿函数与仿函数适配器的更多相关文章

  1. STL标准库-算法-常用算法

    技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性 介绍11种STL标准库的算法,从这11种算法中总结一下算法的基本使用 1.accumulate() 累加 2.for_each( ...

  2. STL标准库-容器-set与multiset

    技术在于交流.沟通,转载请注明出处并保持作品的完整性. set与multiset关联容器 结构如下 set是一种关联容器,key即value,value即key.它是自动排序,排序特点依据key se ...

  3. STL标准库-容器-deque

    技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性. deque双向开口可进可出的容器 我们知道连续内存的容器不能随意扩充,因为这样容易扩充别人那去 deque却可以,它创造了内存 ...

  4. STL标准库-容器-vector

    技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性. 向量容器vector是一个动态数组,内存连续,它是动态分配内存,且每次扩张的原来的二倍. 他的结构如下 一 定义 vector ...

  5. STL标准库-容器-set与map

    STL标准库-容器-set与multiset C++的set https://www.cnblogs.com/LearningTheLoad/p/7456024.html STL标准库-容器-map和 ...

  6. C++STL标准库学习笔记(五)set

    前言: 在这个笔记中,我把大多数代码都加了注释,我的一些想法和注解用蓝色字体标记了出来,重点和需要关注的地方用红色字体标记了出来,这一篇后面主要都是我的记录了,为了防止大片蓝色字体出现,后面就不改蓝色 ...

  7. C++STL标准库学习笔记(三)multiset

    C++STL标准库学习笔记(三)multiset STL中的平衡二叉树数据结构 前言: 在这个笔记中,我把大多数代码都加了注释,我的一些想法和注解用蓝色字体标记了出来,重点和需要关注的地方用红色字体标 ...

  8. C++STL标准库学习笔记(二)二分查找

    二.STL中的二分查找算法 1.binary_search 2.lower_bound 3.upper_bound 记得#include<algorithm>! 前言: 在这个笔记中,我把 ...

  9. C++STL标准库学习笔记(一)sort

    前言: 近来在学习STL标准库,做一份笔记并整理好,方便自己梳理知识.以后查找,也方便他人学习,两全其美,快哉快哉! 这里我会以中国大学慕课上北京大学郭炜老师的<程序设计与算法(一)C语言程序设 ...

随机推荐

  1. 树莓派GPIO

  2. POJ 3744 Scout YYF I(矩阵快速幂优化+概率dp)

    http://poj.org/problem?id=3744 题意: 现在有个屌丝要穿越一个雷区,雷分布在一条直线上,但是分布的范围很大,现在这个屌丝从1出发,p的概率往前走1步,1-p的概率往前走2 ...

  3. python 分数的数学四则运算

    import fractions f1 = fractions.Fraction(, ) f2 = fractions.Fraction(, ) print('{} + {} = {}'.format ...

  4. MongoDB(课时20 游标)

    3.5 游标(重点) 所谓游标就是指数据可以一行行的进行操作,非常类似于ResultSet数据处理.在MongoDB里对游标的控制使用find()函数就可以返回游标.对于返回的游标如果想进行操作,使用 ...

  5. pandas (loc、iloc、ix)的区别

    loc:通过行标签索引数据 iloc:通过行号索引行数据 ix:通过行标签或行号索引数据(基于loc和iloc的混合) 使用loc.iloc.ix索引第一行数据: loc: iloc: ix:

  6. Codeforces 862B - Mahmoud and Ehab and the bipartiteness

    862B - Mahmoud and Ehab and the bipartiteness 思路:先染色,然后找一种颜色dfs遍历每一个点求答案. 代码: #include<bits/stdc+ ...

  7. PWA小记

    前言 中国有不一样的MobileFirst战略,重原生应用,轻移动网页: 移动网页的弱势:页面设计优化有限,用户体验受网络环境影响,网页开启不方便: web优势是产品分发 app优势是产品使用和交互 ...

  8. Redis之有序集合命令

    Redis 有序集合(sorted set) Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员. 不同的是每个元素都会关联一个double类型的分数.redis正是通过 ...

  9. [可能没有默认的字体]Warning: imagettfbbox() [function.imagettfbbox]: Invalid font filename...

    Warning: imagettfbbox() [function.imagettfbbox]: Invalid font filename... [可能没有默认的字体] 例: //putenv('G ...

  10. codeforces 521a//DNA Alignment// Codeforces Round #295(Div. 1)

    题意:如题定义的函数,取最大值的数量有多少? 结论只猜对了一半. 首先,如果只有一个元素结果肯定是1.否则.s串中元素数量分别记为a,t,c,g.设另一个串t中数量为a',t',c',g'.那么,固定 ...