STL源码剖析之仿函数配接器其一之例子解惑

358 阅读2分钟

在《STL源码剖析》一书的第7章第2节提到,binary_function用来呈现二元函数的第一参数类型、第二参数类型,以及返回值类型,定义如下:

// STL规定,每一个Adaptable Binary Function都应继承此类别
template <class Arg1, class Arg2, class Result>
struct Binary_function {
    typedef Arg1 first_argument_type;
    typedef Arg2 second_argument_type;
    typedef Result result_type;
}

一旦某个仿函数继承了它,其用户便可以这样取得该仿函数的各种相应类型

// 以下仿函数继承了binary_function
template <class T>
struct plus : public binary_function<T, T, T> {
    T operator()(const T& x, const T& y, ) const {
        return x + y;
    }
};

// 以下配接器将二元仿函数转化为一元仿函数
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 Operation::second_argument_type& a) const {
        return op(value, a);
    }
};
// 根据构造函数的定义,以上该函数的目的是将Operation函数的第一个参数绑定到一个值,该值是在调用binder1st时设定的,
// 然后因为重载了operator(),因此在调用binder1st对象时,被传入的参数会被当做Operation函数的第二个参数,
// 第一个参数已经固定为上文的value,也就是return op(value, a)中的value

实例分析: 现在要对一个vector中的每一个元素施行策略:(x + 2) * 3,可以这么写:

std::vector<int> v{1, 2, 3};
std::for_each(v.begin(), v.end(), compose1(bind2nd(multiplies<int>(), 3), bind2nd(plus<int>(), 2)));

// 解释
bind2nd 与上面的binder1st类似,是绑定第二个参数为设定的值,比如上文中的32.
比如bind2nd(plus<int>(), 2) ,plus 函数对象的第二个参数为2,同时bind2nd会将该二元函数转为一元函数:
typename Operation::result_type operator()(const Operation::first_argument_type& a) const {
    return op(a, value);
}
// a固定为2,value是通过调用该函数对象时传入的值,在这里就是vector的每一个元素
// 最终bind2nd(plus<int>(), 2)等效于 x + 2
// compose1的意思是先执行后一个表达式,得到的结果代入前一个表达式,因此这里的compose1即为 (x + 2) * 3

compose1源码如下,位于stl_function.h中:

template <class _Operation1, class _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));
  }
};

template <class _Operation1, class _Operation2>
inline unary_compose<_Operation1,_Operation2> 
compose1(const _Operation1& __fn1, const _Operation2& __fn2)
{
  return unary_compose<_Operation1,_Operation2>(__fn1, __fn2);
}