问题先由一个很简单的需求开始: 给一个整数vector, 输出所有小于10的整数, 于是有了下面的函数
void filter(const std::vector *numbers) {
for (int number : *numbers) {
if (number < 10) {
std::cout << number << std::endl;
}
}
}
新需求: 要小于指定的数字。那么只要多加一个参数就好
void filter(const std::vector *numbers, int less_than_val) {
for (int number : *numbers) {
if (number < less_than_val) {
std::cout << number << std::endl;
}
}
}
新需求: 可以指定不同的操作, 比如 大于、小于、等于。这个时候需要把 输出number 这个逻辑抽象出来, 其实就是给定两个数字, 经过一个判断, 然后决定是否输出第一个数字, 所以可以将该逻辑抽象为一个固定形式的函数 bool comp(int a, int b)
, 那么如何来指定这个函数到底用哪个呢? 这里就要用到 函数指针
了:
#include
#include
#include
#include
#include
// 注意 函数指针 的写法, 这里参数名当然可写可不写
void filter(const std::vector *numbers, int filter_val, bool(*comp)(int, int)) {
for (int number : *numbers) {
if (comp(number, filter_val)) {
std::cout << number << std::endl;
}
}
}
bool bigger(int a, int b) {
return a > b;
}
bool equal(int a, int b) {
return a == b;
}
int main() {
int numbers_arr[3] = {1, 2, 11};
std::vector numbers(numbers_arr, numbers_arr + 3);
// 传入bigger函数, 输出比10大的数字
filter(&numbers, 10, bigger);
return 0;
}
新需求: 消除具体的容器类型和具体的元素类型
在这之前先介绍一下function object
, 它其实是一个实例对象, 但是由于它重写了function call
运算符, 也就是()
括号操作符, 所以它可以被当成普通函数一样来使用, 举个例子来说
class Person {
public:
void operator() () {
std::cout << "重载了function call 也就是()操作符" << std::endl;
}
};
那么如果建立一个它的实例对象 Person p
, 那么就可以这样来使用p()
, 理解起来比较简单, 一些标准库中的常见function object
- 算术运算: plus
, modules ... - 关系运算: less
, less_equal , greater ... - 逻辑运算: logical_and
...
都是泛型算法, 使用的时候需要填一下type
那么function object
的一些用途, 比如sort
函数, 就可以塞一个关系运算的function object
, 比如sort(x.begin, x.end, less
, 其实这里的less
的含义就是less这个泛型类的重载过后的function call
也就是()
方法, 你自己写一个泛型函数接受两个type
值比较大小, 也能起到一样的效果, 不过库文件中的less
应该是把常见的类型的比较都帮你实现了, 所以我猜测这里sort
函数的第三个参数其实接收的是一个接受两个值返回bool的函数指针而已。
了解了function object
以后, 再了解一下function object adapater
, 书中介绍的是bind adapter
, 这里就简单讲一下这个adapter
, 刚才知道function object
重载的()
函数是有参数的, 在我们的场景下, 需要比较两个数的大小, 所以其接受的是两个数, 但是find_if()泛型算法的第三个参数要求的是一个一元运算符, 所以不能直接把两个参数的function object
直接塞进去, 而是要想办法把它变成一个一个参数的function object
, 实现的方法就是使用bind adapter
的bind2nd()
函数, 它可以绑定一个function object
的第二个参数的值。
了解完什么是function object
和什么是function object adapter
以后, 就可以把刚才的filter函数
继续改写成与容器无关和容器类型无关的函数了
- 与容器无关: 通过传入iterator
- 与类型无关: 通过泛型
那么为什么还要引入function object
呢, 仅仅是为了适配find_if
泛型算法吗, 我觉得这可能也是合理的吧, 毕竟既然标准库已经有比较运算的泛型算法了, 自己再去实现一遍没有任何好处, 还有可能会犯错。
下面就是最终的代码啦~
#include
#include
#include
#include
#include
#include
// InputIterator : 输入的 iterator 类型
// OutputInterator: 输出的 iterator 类型
// ElemType: 元素类型
// Comp: function object 类型, 理应是一个 关系运算
template
OutputIterator filter(InputIterator begin, InputIterator end, OutputIterator at, const ElemType &val, Comp pred) {
while ((begin = std::find_if(begin, end, std::bind2nd(pred, val))) != end) {
std::cout << *begin << std::endl;
*at++ = *begin++;
}
};
int main() {
int numbers_arr[3] = {1, 2, 11};
std::vector numbers(numbers_arr, numbers_arr + 3);
std::vector result(3); // 因为函数中是移位赋值, 所以需要提前预留够足够的内存
filter(numbers.begin(), numbers.end(), result.begin(), 10, std::greater());
return 0;
}
filter函数可能写的可读性没那么强, 它等价于以下这种形式
// InputIterator : 输入的 iterator 类型
// OutputInterator: 输出的 iterator 类型
// ElemType: 元素类型
// Comp: function object 类型, 理应是一个 关系运算
template
OutputIterator filter(InputIterator begin, InputIterator end, OutputIterator at, const ElemType &val, Comp pred) {
while (begin != end) {
begin = std::find_if(begin, end, std::bind2nd(pred, val));
std::cout << *begin << std::endl;
*at = *begin;
at++;
begin++;
}
};
我理解这里引出知识点的逻辑是这样的 我想要使用泛型
-> 我需要一个泛型的关系比较函数(不想自己实现)
-> 我想使用find_if来查找
-> 使用adapter来结合泛型关系比较函数和find_if
以上。