[cpp][g.p] STL中的约束器相关总结

摘要: STL里有仿函数的概念,而在应用仿函数的时候,仿函数与仿函数之间的适配引出了约束器的概念。这一节主要叙述一下一元函数对象基类unary_function、二元函数对象基类binary_function,以及两个约束器binder1st与binder2nd,同时给出一个场景,分析实现原理。

1:template <class Arg, class Result> struct unary_function;
2:template <class Arg1, class Arg2, class Result> struct binary_function;
3: template <class Operation> class binder1st;
4: template <class Operation> class binder2nd;
5: template <class Operation, class T>
     binder2nd<Operation> bind2nd (const Operation& op, const T& x);
6: template <class Operation, class T>
          binder1st<Operation> bind1st (const Operation& op, const T& x);

        unary_function是一元函数对象的基类,unary_function作为一个基类并没有实现operator(),仅仅含有两个数据成员,operator()是由子类根据具体情况实现的。binary_function则是二元函数对象的基类,同样没有实现operator().
template < class Arg, class Result >
struct unary_function {
    typedef Arg argument_type;
    typedef Result result_type;
};
// unary_function example
# include <iostream >
# include <functional >
using namespace std;
struct IsOdd : public unary_function < int, bool > {
    bool operator() ( int number) { return (number % 2 == 1);}
};
int main () {
    IsOdd IsOdd_object;
    IsOdd : :argument_type input;
    IsOdd : :result_type result;
    cout << "Please enter a number: ";
    cin >> input;
    result = IsOdd_object (input);
    cout << "Number " << input << " is " << (result ? "odd" : "even") << ".\n";
    return 0;
}
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 example
# include <iostream >
# include <functional >
using namespace std;
struct Compare : public binary_function < int, int, bool > {
    bool operator() ( int a, int b) { return (a ==b);}
};
int main () {
    Compare Compare_object;
    Compare : :first_argument_type input1;
    Compare : :second_argument_type input2;
    Compare : :result_type result;
    cout << "Please enter first number: ";
    cin >> input1;
    cout << "Please enter second number: ";
    cin >> input2;
    result = Compare_object (input1,input2);
    cout << "Numbers " << input1 << " and " << input2;
    if (result)
        cout << " are equal.\n";
    else
        cout << " are not equal.\n";
    return 0;
}
   
         binder1st 与 binder2nd 的作用都是由二元函数对象得到一个一元函数对象,他们是"函数对象的适配器",其中binder1st 将第一个参数固定为一个固定的数值,binder2nd 将第二个参数固定为一个固定的数值。具体使用的时候,将某一个参数绑定为操作的第一个或者第二个参数,要根据操作语义而定,见如下示例。而bind2ndbind1st就是为了产生这两种对象而存在的。
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); }
};
// binder1st example
# include <iostream >
# include <functional >
# include <algorithm >
using namespace std;
int main () {
    binder1st < equal_to < int > > equal_to_10 (equal_to < int >(), 10);  // 这里将10版定位(equal_to<int>())操作的第一个参数
    int numbers[] = { 10, 20, 30, 40, 50, 10};
    int cx;
    cx = count_if (numbers,numbers + 6,equal_to_10);
    cout << "There are " << cx << " elements equal to 10.\n";
    return 0;
}
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
        operator() ( const typename Operation : :first_argument_type & x) const
    { return op(x,value); }
};
// binder2nd example
# include <iostream >
# include <functional >
# include <algorithm >
using namespace std;
int main () {
    binder2nd < less < int > > IsNegative (less < int >(), 0); // 这里将0绑定为(less<int>())操作的第二个参数
    int numbers[] = { 10, - 20, - 30, 40, - 50};
    int cx;
    cx = count_if (numbers,numbers + 5,IsNegative);
    cout << "There are " << cx << " negative elements.\n";
    return 0;
}
  
下面给出一个场景分析:
现在定义一个函数对象如下inserter ,完成的功能是向vector中插入数据,如下可见在实际使用的时候传递两个参数。
struct inserter {
public :
    void operator()( double n, vector < double > & v) { v.push_back(n); }
};
inserter f;
vector < double > vd;
f( 1. 0, vd); //两个参数
如果现在有一个算法append是如下定义的,向容器中添加一些数据成员。
template < typename Functor >
void append(Functor f) {
    double x;
    while(cin >>x)
        f(x);
}
        这时,你的insert就用不上了,因为append的functor只要求一个参数。如果你的inserter本来是一个很复杂的操作,那么如果重写一个就很麻烦。不过有一个简单的办法是再定义一个inserter_adapter。
template < typename Functor >
struct inserter_adapter {
    inserter_adapter( const Functor & f, vector < double > & v)
        :f_(f), v_(v) {}
    void operator()( double x) { f_(x, v_);}
    Functor f_;
    vector < double > & v_;
};
inserter f;
vector < double > vd;
inserter_adapter ia(f, vd);  //将vd绑定到操作符f的第二个参数上
append(ia);
        由以上的场景分析,可以看出inserter_adapter 充当了一个适配器的角色,解决了二元函数对象无法适应一元函数对象操作的情况。因为inserter_adapter 将容器参数对象绑定到了操作符的第二个参数上。
        以上方案实现的是将二元函数对象转化为一元函数对象,这种转化是通过绑定一个操作对象。而在STL中,这种参数绑定操作可以通过以上介绍的binder2nd或者binder1st实现。
struct inserter :public std::binary_function<double, vector<double>&, void> {
public:
    void operator()(double n, vector<double> & v) { v.push_back(n); }
};
template<typename Functor>
void append(Functor f) {
    double x;
    while(cin>>x)
        f(x);
}
inserter f;
vector<double> vd;
append(std::bind2nd(f, vd)); //bind2nd创建了一个binder2nd对象,将参数vd绑定到操作符f的第二个参数
 

你可能感兴趣的:(STL)