STL源码分析--functional

Posted 后端技术小屋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STL源码分析--functional相关的知识,希望对你有一定的参考价值。




  • 1 相关头文件

  • 2 一元函数对象

    • 2.1 binder1st/binder2nd

    • 2.2 unary_compose

    • 2.3 pointer_to_unary_function

    • 2.4 mem_fun_t

    • 2.5 其他

  • 3 二元函数对象

    • 3.1 mem_fun1_t

    • 3.2 其他


1 相关头文件

functional
functional.h
stl_function.h 

2 一元函数对象

一元函数对象,是指这类函数对象只接受一个参数并返回一个参数。

unary_function为STL中所有一元函数对象的基类。它定义了一元函数对象的输入/输出参数类型,其中argument_type为输入参数类型,result_type为输出参数类型,这两种类型由输入模板参数推导而来。

template <class _Argclass _Result>
struct unary_function {

  typedef _Arg argument_type;
  typedef _Result result_type;
};

下面介绍几种有意思的一元函数对象。

2.1 binder1st/binder2nd

binder1st的功能:将某个参数值绑定到可调用对象(Callable type,函数对象/函数指针/std::function)的第一个参数中。同理,binder2nd将某个参数值绑定到可调用对象的第二个参数中。

有了binder1st/binder2nd,便可利用已有的可调用对象生成各种变体,避免了重新定义带来的代码冗余。

    auto f = [](double a, double b){ return 2*(a+b); };
    double a0 = 1.0;
    double b0 = 2.0;
    auto uf1 = std::binder1st(f, a0);
    auto uf2 = std::binder2nd(f, b0);
    
    std::cout << uf1(3.0) << std::endl// 执行f函数,参数1:被绑定的a0, 参数2:3.0
    std::cout << uf2(4.0) << std::endl// 执行f函数,参数1: 4.0,      参数2:被绑定的b0

binder1st内部嵌套了一个可调用对象op。生成binder1st对象时,其内部记录传入的可调用对象和绑定参数。当执行operator()时,其调用可调用对象并返回结果。

template <class _Operation
class binder1st
  :
 public unary_function<typename _Operation::second_argument_type,
                          typename _Operation::result_type> {
protected:
  _Operation op;
  typename _Operation::first_argument_type value;
public:
  binder1st(const _Operation& __x,
            const typename _Operation::first_argument_type& __y)
      : op(__x), value(__y) {}
  typename _Operation::result_type
  operator()(const typename _Operation::second_argument_type& __x) const 
{
    return op(value, __x); 
  }
};

2.2 unary_compose

将两个属于unary_function的函数对象级联。假设函数对象分别为op1op2, 那么unary_compose的执行结果为op1(op2(x))

因此unary_compose的输入参数类型为_Operation2::argument_type,输出参数类型为_Operation1::result_type。并且要求在类型上_Operation2::result_type可隐式转换成_Operation1::argument_type

template <class _Operation1class _Operation2>
class unary_compose
  :
 public unary_function<typename _Operation2::argument_type,
                          typename _Operation1::result_type> 
{
protected:
  _Operation1 _M_fn1;
  _Operation2 _M_fn2;
public:
  unary_compose(const _Operation1& __x, const _Operation2& __y) 
    : _M_fn1(__x), _M_fn2(__y) {}
  typename _Operation1::result_type
  operator()(const typename _Operation2::argument_type& __x) const 
{
    return _M_fn1(_M_fn2(__x));
  }
};

2.3 pointer_to_unary_function

将一个指向一元函数的指针封装成一元函数对象。

template <class _Argclass _Result>
class pointer_to_unary_function :
 public unary_function<_Arg, _Result> {
protected:
  _Result (*_M_ptr)(_Arg);
public:
  pointer_to_unary_function() {}
  explicit pointer_to_unary_function(_Result (*__x)(_Arg)) : _M_ptr(__x) {}
  _Result operator()(_Arg __x) const return _M_ptr(__x); }
};

2.4 mem_fun_t

将类内非静态成员函数封装成一元函数对象。

对于mem_fun_t对象,其构造时记录指向类_Tp内非静态成员函数(输入参数个数为零)的指针,执行时,传入指向_Tp对象的指针__p,调用(__p->*_M_f)()

在C++中,函数指针类似这种形式:int (*f)(int, double)f指向一个入参为int, double,出参为int的函数。调用函数指针f时,只需指定输入参数。

而成员函数指针类似于:int (T::*mf)(int, double)mf指向一个入参为int, double, 出参为int的成员函数。调用成员函数指针时,除了需指定输入参数外,还需指定T对象指针,因为类中非静态成员函数是和该类对象绑定的。

template <class _Retclass _Tp>
class mem_fun_t :
 public unary_function<_Tp*,_Ret> {
public:
  explicit mem_fun_t(_Ret (_Tp::*__pf)()) : _M_f(__pf) {}
  _Ret operator()(_Tp* __p) const return (__p->*_M_f)(); }
private:
  _Ret (_Tp::*_M_f)();
};

其他变体:

  • const_mem_fun_t: 将类内非静态的const成员函数封装成一元函数对象
  • mem_fun_ref_t: 类似 mem_fun_t,区别在于调用 mem_fun_t时传入的是类对象指针,而调用 mem_fun_ref_t时传入的是类对象引用。
  • const_mem_fun_ref_t: const_mem_fun_tmem_fun_ref_t的组合

2.5 其他

  • negate: 取负操作。实现中对 _Tp对象执行 operator-操作,因此negate的输入模板参数不止为数字,也可是重载了 operator-操作符的其他任何类型。
  • logical_not: 逻辑取反。函数对象输入参数类型为 _Tp, 输出参数类型为 bool。实现中对 _Tp对象执行 operator!操作,对象可重载该操作符。
  • unary_negate: 同 negate相似,只不过带了一个判断条件。输入参数 __x, 返回布尔值 !_M_pred(__x)

3 二元函数对象

二元函数对象接受两个参数,返回一个结果值。

STL中定义的所有二元函数对象都继承自binary_function, 其中

  • first_argument_type: 第一个输入参数类型
  • second_argument_type: 第二个输入参数类型
  • result_type: 输出参数类型
template <class _Arg1class _Arg2class _Result>
struct binary_function {

  typedef _Arg1 first_argument_type;
  typedef _Arg2 second_argument_type;
  typedef _Result result_type;
}; 

3.1 mem_fun1_t

mem_fun_t类似,将一个成员函数指针封装成函数对象。区别在于mem_fun_t中成员函数入参个数为零,而mem_fun1_t中成员函数入参个数为1,这样的话调用mem_fun1_t时,需同时传入成员函数的入参和成员函数所在类对象指针。

其他变体:const_mem_fun1_t/mem_fun1_ref_t/const_mem_fun1_ref_t,功能与mem_fun_t的变体类似,这里跳过

3.2 其他

以下二元函数对象顾名思义,本质上是对各种操作符的封装。

plus
minus
multiplies
divides
modulus
equal_to
not_equal_to
greater
less
greater_equal
less_equal
logical_and
logical_or
logical_not 

binary_compose: 功能同unary_compose。区别在于将三个二元函数对象级联,输出__fn1(__fn2(__x), __fn3(__x))




以上是关于STL源码分析--functional的主要内容,如果未能解决你的问题,请参考以下文章

STL 红黑树源码分析

STL源码拆解基于源码分析forward_lsit容器实现(详细!)

STL源码拆解基于源码分析forward_lsit容器实现(详细!)

STL源码拆解基于源码分析forward_lsit容器实现(详细!)

STL源码拆解基于源码分析forward_lsit容器实现(详细!)

Android 插件化VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )(代码片段