C++ 的函数对象 (1)

312 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

本文承接上回所说的C++的仿函数

函数对象

C++中的函数对象,是一个非常有用的东西

在STL中,也有许多用到函数对象的地方

比如 sort(begin, end, cmp) 在执行比较操作的时候,sort 会调用cmp来比较两个对象的关系

sort调用的是二元谓词,对应者的函数应该要有两个传参

谓词可以接受的形式:


bool cmp(int a, int b) {
  return a > b;
}
struct cmp {
  bool operator()(int a, int b) { return a > b;}
}
auto cmp = [](int a, int b) -> bool {
  return a > b;
}

lambda 和 函数对象

上回介绍了lambda用类似于宏的方式来进行书写
在c++中应该还是避免用宏的
那么lambda又是仿函数,和模板又可以迸发出什么样子的火花呢?

先看一段例子

int main() {
  place _1, _2;
  std::vector<int> ve{1, 2, 3, 4, 5};
  std::for_each(ve.begin(), ve.end(), std::cout << _1);
}

欸,这段例子中用到了一个for_each, 第三个参数应该要是一个函数对象
可是我们却是一个表达式,这个可以过编译吗?

答案是可以的。

用到的知识点也很简单

那就是 表达式的值

我们知道,在C++中,一个表达式是有值的。

比如:

int a = (2 - 3);

2 - 3 是一个表达式 用 2 - 3 的值来初始化 变量 a

这个有什么用呢?

我们的表达式是不是需要运算符来进行运算所得到操作

运算符可以分为一元、二元和三元运算符

重载运算符 !

template <typename T> struct func {
  void operator()(T x) { std::cout << "x" << x << "\n"; }
};
struct tmp {};
template <typename T> func<T> operator+(tmp t, T value) { return func<T>(); }
int main() {
  tmp a, b, c;
  auto t = a + 1;
  t(1);
}

上述代码可以清楚的看成,tmp是一个包裹类,负责把一个式子的运算结果包裹一下,然后返回一个函数对象

至此,我们就可以实现一些操作了

比如:

template <typename T> struct func {
  void operator()(T x) { std::cout << "x" << x << "\n"; }
};
struct tmp {};
func<int> operator<<(std::ostream &os, tmp tm) { return func<int>(); }
int main() {
  tmp a, b, c;
  auto t = std::cout << a;
  t(1);
}

就可以实现前面所展示的第三个参数是 std::cout << _1 这样的操作

更完整点的例子

我们再来丰富一下自己的这个 类,然后和STL配合起来使用

#include <bits/stdc++.h>

template <typename T> struct eval {
  std::ostream &os;
  T &_1;
  enum TYPE { COUT, ADD, SUB, GREATER, LESS, COMPARE };
  TYPE type;
  eval(std::ostream &os, T _1, TYPE type) : os(os), _1(_1), type(type) {}
  template <typename P> bool operator()(P &t) {
    switch (type) {
    case COUT:
      os << t << " ";
      break;
    case ADD:
      t += _1;
      break;
    case COMPARE:
      return _1 == t;
    case SUB:
      t -= _1;
    }
    return true;
  }
  template <typename A, typename B> bool operator()(const A &a, const B &b) {
    switch (type) {
    case GREATER:
      return a > b;
    case LESS:
      return a < b;
    case COMPARE:
      return a == b;
    }
  }
};
struct place {};
template <typename T> eval<T> operator+=(place p, T val) {
  return eval<T>(std::cout, val, eval<T>::ADD);
}
template <typename T> eval<T> operator-=(place p, T val) {
  return eval<T>(std::cout, val, eval<T>::SUB);
}
template <typename T> eval<T> operator>(place p, T val) {
  return eval<T>(std::cout, val, eval<T>::GREATER);
}
template <typename T> eval<T> operator<(place p, T val) {
  return eval<T>(std::cout, val, eval<T>::LESS);
}
template <typename T> eval<T> operator==(place p, T val) {
  return eval<T>(std::cout, val, eval<T>::COMPARE);
}
eval<int> operator<<(std::ostream &os, place place) {
  return eval<int>(os, 0, eval<int>::COUT);
}
int main() {
  place _1, _2;
  std::vector<int> ve{1, 2, 3, 4, 5};
  std::for_each(ve.begin(), ve.end(), _1 += 2);
  std::for_each(ve.begin(), ve.end(), std::cout << _1);
  std::cout << " \n";

  std::sort(ve.begin(), ve.end(), _1 > _2);
  std::for_each(ve.begin(), ve.end(), std::cout << _1);
  std::cout << " \n";

  std::sort(ve.begin(), ve.end(), _1 < _2);
  std::for_each(ve.begin(), ve.end(), std::cout << _1);
  std::cout << " \n";

  std::for_each(ve.begin(), ve.end(), _1 -= 2);
  std::for_each(ve.begin(), ve.end(), std::cout << _1);
  std::cout << " \n";

  std::cout << std::count_if(ve.begin(), ve.end(), _1 == 2);
  return 0;
}

上述代码虽然写得很丑,但是基本功能还是实现了的
那么,上面的表达式都很简单啊,都没有复杂一点的
学过编译原理都应该知道
复杂表达式也是可以由简单表达式所构成的

比如

        "*"
       /   \
"1 + 2"     "( 3 - 4 )"

那么,我们是不是能够利用模板编程来实现这样的东西呢 ?