小王职场记STL(3)lambda表达式的本质

上篇文章回顾:

STL理解(1)容器

STL理解(2) 算法

经过上面你了解 ,你应该知道了

  1. stl使用大量inline函数,inline函数定义光明正大放到头文件保证不会出错
    ------想象普通函数定义为什么不可以,想一想 stl的普通函数为什么可以?

  2. 因为放到头文件 模板+重载 构成预编译的多态

1837968-d96fd8a8b12dd38c.png
1548314223531.png

本节 介绍的是另一一个点,stl 为什么提供强大的适配能力 。

提供了函数对象,也就是lambda表达式的本质

看一行代码

std::vector<int> vec{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
//计算容器中小于等于3的元素个数
cout << count_if(vec.begin(), vec.end(), bind2nd(less_equal<int>(), 3));
int count  = std::count_if(vec.begin(), vec.end(), [](int x) {return x >  3;})

看不懂没 关系 继续分析

  • 函数适配器(很重要)

第一个问题:理解typedef含义

  • struct unary_function

定义一个类,然后 里面有几个个成员,没有看出里面的有什么作用呀?

提前定义好成员变量,这样才能实现类型多态的调用

binary_function is a base class for creating function objects with two arguments.

// 一元函数的参数类型和返回值类型
template <class _Arg, class _Result>
//stl_function.h::38
struct unary_function {

  typedef _Arg argument_type; //类的成员函数 是变量的类型

  typedef _Result result_type; //类的成员函数 是变量的类型
};
// 二元函数的第一个参数类型和第二个参数类型,以及返回值类型
template <class _Arg1, class _Arg2, class _Result>
struct binary_function {
  typedef _Arg1 first_argument_type;
  typedef _Arg2 second_argument_type;
  typedef _Result result_type;
};  

std::unary_function<int,bool>

### 应用于仿函数,function adapter

> 对返回值进行逻辑否定:not1, not2

> 对参数进行绑定:bind1st, bind2nd

> 用于函数合成:compose1, compose2

> 用于函数指针:ptr_fun

> 用于成员函数指针:mem_fun, mem_fun_ref

成员类型 定义 注释
argument_type 第一个模板参数 (Arg) ()重载函数的参数类型
result_type 第二个模板参数(Result) ()重载函数的返回值类型
成员类型 定义 注释
first_argument_type 第一个模板参数(Arg1) ()重载函数的第一个参数类型
second_argument_type 第一个模板参数 (Arg2) ()重载函数的第二个参数类型
return_type 第一个模板参数(Result) ()重载函数的返回值类型
  • class binder2nd
//第一次分析:class binder2nd 声明一个类 这个语法你肯定明白
//第二次分析:class binder2nd:public unary_function 
//binder2nd继承来了模板类unary_function,typename _Operation::first_argument_type 是参数类型 
//vector<int> int就是类型  vector就是模板类  这个对比应该明白  
//第三次分析:  _<typename _Operation::first_argument_type
//请问 class  _Operation是任意类, typename first_argument_type任意类型之间关系是什么?
//但是在stl语法中 typename T 代码是 成员变量的类型(int ,char*)   class T 代表类的类型
//vector:base, a.m_i 
//T::T
//第四次分析:
// _Operation::second_argument_type 你怎么确定  类_Operation里面一定有成员变量 second_argument_type
// 
//binary_function is a base class for creating function objects with two arguments.
//stl 规定 函数对象必须这个类, 这样函数对象之间(虽然不是继承,但是可以调用),但是相互使用了(佩服呀,因此函数对象适配器,可以适配任何同类对象)
// 这就是编译期间的多态

////第五次分析:重载 返回值 operator()(参数)
//typename _Operation::result_type operator()(const typename _Operation::first_argument_type& __x) const 
 //返回的结果是不是具体类型 是模板  result_type是unary_function实现的
 //op(__x, value) 

////第6次分析:
//binder2nd::unary_function
//_Operation::binary_function
//这是一次更强大的适配
//这个是第六次分析   关键点 
//value 是 binder2nd是创建时候调用构造时候设置的,
//_x 调用函数关系()设置的
//A a(10)  构造(a) 
// a(20)   函数调用(a,b)
/**
此函数适配器必须要继承自unary_function对象,满足可配接性。
解释一下可配接性。less_equal类继承自binary_function,便有了内部嵌套类型second_argument_type,
而这个类型正好需要用在binder2nd中,以保存(绑定)某个参数。这样,less_equal就变为了可配接的。
纵观整个适配器系统,基本上都是把某个对象或指向对象的指针封装在一个适配器类中,对适配器的操作最终都会传递到对所包含对象的操作
**/

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) // 绑定的第二个数
  {

  }
  typename _Operation::result_type //返回的结果是不是具体类型 是模板  result_type是unary_function实现的

 
  typename _Operation::result_type operator()(const typename _Operation::first_argument_type& __x) const 
  {
     return op(__x, value);  
     //这个是第六次分析   关键点 
     //value 是 binder2nd是创建时候调用构造时候设置的,
     //_x 调用函数关系()设置的
     //A a(10)  构造(a) 
     // a(20)   函数调用(a,b)
    
  }
};
  • 测试程序;
std::vector<int> vec{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
//计算容器中小于等于3的元素个数
cout << count_if(vec.begin(), vec.end(), bind2nd(less_equal<int>(), 3));
int count  = std::count_if(vec.begin(), vec.end(), [](int x) {return x >  3;})
1837968-3460515e87bce274.png
1548330412420.png

lambda表达式的本质(函数对象)

lambda表达式就是一个函数对象

当编写了一个lambda表达式的时候,编译器将该表达式翻译成一个未命名类的未命名对象

int num = 100;
auto f = [num](){return num; };//等价于F

class F
{
public:
    F(int n) :num(n){}
    int operator()() const { return num; }
private:
    int num;
};

相关代码:

猜你喜欢

转载自blog.csdn.net/weixin_34081595/article/details/87054854