模板(十一):函数配接器

标准库提供用于简化使用函数对象的繁杂的过程的类和函数

为什么是函数对象

  • 可以把一个函数和一个值捆绑到单个实体中

    • 如果把值n放到具有一个文件作用域(file scope)的变量中也可以使用函数
1
2
3
4
5
6
static int n;
static int add_n(int x) {
return x + n;
}
transform(a, a+100, a, add_n);
  • 不方便
  • 标准库提供

  • 调用:

    • transform(a, a+100, a, bind1st(plus<int>(), n));
  • bind1st(plus<int>(), n)使用标准库创建了一个函数对象

用于內建操作符的函数对象

  • plus是一个类型
  • plus<int>()是一个等价于类型为plus的匿名对象的表达式
    • 把两个类型为int的值相加,以和作为结果
1
2
3
4
plus<int> p;
int k = p(3, 7);
int k = (plus<int>())(3, 7);
  • 三个类型成员
    • first_argument_type
    • second_argument_type
    • result_argument_type
  • 标准库包含內建操作符所需要的绝大部分函数对象

绑定者(Binders)

  • 创建能够记住一个值并把该值加到参数上的函数对象
    • bind1st和bind2nd库模板函数
  • bind1st创建一个函数对象,绑定函数的第一个参数

  • 函数对象bind1st(f, x)只接受一个参数

    • f有接受两个参数的operator()
    • x是可以作为f的第一个参数的值
  • (bind1st(f, x))(y)f(x, y)相同的值

  • bind1st(plus<int>(), n)(y)等价于n+y

1
2
3
4
5
6
7
8
// p是一个将两个整数相加的函数对象
plus<int> p;
// b是一个将参数加到n上去的函数对象
some_type b = bind1st(p, n);
// 初始化z为n+y
int z = b(y);
  • b的类型?
    • sometype b = bind1st(p, n); -> binder1st<plus<int> > b = bind1st(p, n)
  • p是一个函数对象,负责把两个数相加

  • b是一个函数独享,负责把n绑定在两个被相加的数的第一个数上

深入探讨

  • 写bind1st的声明
1
2
3
4
5
6
template <class T>
class binder1st {
public:
T1 operator() (T2)
// ...
};
  • 约定遵循类型规定的类才能使用binder1st
    • first_argument_type
    • second_argument_type
    • result_argument_type
1
2
3
4
5
6
7
template <class T>
class binder1st {
public:
binder1st(const T&, const T::first_argument_type&);
T::result_type operator() (const T::second_argument_type*);
// ...
};

接口继承

  • C++库如模板类plus的函数对象类都定义了成员类型first_argument_type、second_argument_type和result_type
  • 具有共同的基类: binary_function
1
2
3
4
5
6
7
8
template <class A1, class A2, class R>
class binary_function {
public:
typedef A1 first_argument_type;
typedef A2 second_argument_type;
typedef R result_type;
// ...
};
  • plus定义
1
2
3
4
5
6
7
template <class T>
class plus: public binary_function<T, T, T> {
public:
T operator() (const T& x, const T& y) const {
return x + y;
}
};
  • 标准库的unary_function基类
1
2
3
4
5
6
template <class A, class R>
class unary_function {
public:
typedef A argument_type;
typedef R result_type;
};
  • 可以作为negate的基类
1
2
3
4
5
6
7
template <class T>
class negate: public unary_function<T, T> {
public:
T operator() (const T& x) const {
return -x;
}
};

使用这些类

  • 标准库容器c,x是某个放在其中的值
    • find(c.begin(), c.end(), x);
    • 生成一个指向c中第一个x相等的迭代器或逾尾迭代器(找不到x)
  • 使用函数配接器以更精巧的方法

    • find_if(c.begin(), c.end(), bind1st(equal_to<c::value_type>(), x));
    • 约定了所有库容器用名为value_type的成员类型表示元素的类型
  • 想知道是否每个元素e都存在e > x,采用bind2nd

    • find_if(c.begin(), c.end(), bind2nd(greater<c::value_type>(), x));
  • 容器x和w具有相同个数的元素

    • transform(v.begin(), v.end(), w.begin(), v.begin(), plus<v::value_type>());
    • 将相应元素相加,结果存放到第四个参数指向开始位置的序列中
  • 找出每个指向包含”C”的以null结尾的字符串的指针并用指向字符串”C++”的指针替换

    • char* p[N];
    • replace_if(p, p+N, not1(bind2nd(ptr_fun(strcmp), "C")), "C++");
      • ptr_fun创建一个适合于传递给strcmp的函数对象
      • bind2nd使用这个对象创建另一个函数对象: 用”C”和它的参数进行比较
      • not1否定判断的意义

Summary

这些程序不比相应的常规程序运行得慢

  • 理论上可以更快: 标准库的一部分,编译器在适当的时候可以识别并产生高效的代码

  • 使得一次处理整个容器成为现实,不必采用循环逐个处理单个元素


《C++沉思录(Cplusplus Thinking)》笔记