# 函数对象

函数对象分类:

  • 发生器
  • 一元函数
  • 二元函数
  • 一元判定函数
  • 二元判定函数

函数对象的基本写法:重载 operator()

#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
class CSum {
   private:
    int sum;
   public:
    CSum() { sum = 0; }
    // 注意写法:
    void operator()(int n) { sum += n; }
    int getSum() { return sum; }
};
int main() {
    vector<int> v;
    for (int i = 1; i <= 100; i++) {
        v.push_back(i);
    }
    CSum sObj = for_each(v.begin(), v.end(), CSum());
    cout << sObj.getSum() << endl;
    return 0;
}

# 函数适配器

# 绑定

用来在函数对象上左一个扩充,依然使用之前的函数对象,但是使用了延伸的功能

bind1st()

#include <algorithm>
#include <functional>
#include <iostream>
using namespace std;
int main() {
    int numbers[] = {10, 20, 30, 40, 50, 60};
    int cx;
    cx = count_if(numbers, numbers + 6, bind2nd(less<int>(), 40));
    cout << "There are " << cx << " elements that are less than 40.\n";
    cx = count_if(numbers, numbers + 6, bind1st(less<int>(), 40));
    cout << "There are " << cx << " elements that are not less than 40.\n";
    return 0;
}

分析

less() 是一个二元函数对象, less(a, b) 表示判断 a < b 是否成立。

所以 bind2nd(less<int>(), 40) 相当于 x < 40 是否成立,用于判定那些小于 40 的元素。

bind1st(less<int>(), 40) 相当于 40 < x 是否成立,用于判定那些大于 40 的元素。

# bind()

bind1st () 和 bind2nd (),在 C++11 里已经 deprecated 了。bind () 可以替代他们,且用法更灵活更方便。

上面的例子可以写成下面的形式:

#include <iostream>
#include <algorithm>
#include <functional>
using namespace std;
int main() {
    int numbers[] = {10, 20, 30, 40, 50, 10};
    int cx;
    cx = count_if(numbers, numbers + 6, bind(less<int>(), std::placeholders::_1, 40));
    cout << "There are " << cx << " elements that are less than 40.\n";
    cx = count_if(numbers, numbers + 6, bind(less<int>(), 40, std::placeholders::_1));
    cout << "There are " << cx << " elements that are not less than 40.\n";
    return 0;
}

std::placeholders::_1 是占位符,标定这个是要传入的参数。
所以 bind() 不仅可以用于二元函数,还可以用于多元函数,可以绑定多元函数中的多个参数,不想绑定的参数使用占位符表示。
此用法更灵活,更直观,更便捷。

# 取反

std::not1 和 std::not2 是用来把,符合某种特殊条件的『函数对象』转换为反义「函数对象」的函数。

not1 是构造一个与谓词结果相反的一元函数对象。
not2 是构造一个与谓词结果相反的二元函数对象。

// not1 example
#include <algorithm>   // std::count_if
#include <functional>  // std::not1
#include <iostream>    // std::cout
struct IsOdd {  //是否为奇数
    bool operator()(const int& x) const { return x % 2 == 1; }
};
int main() {
    int values[] = {1, 2, 3, 4, 5};
    int cx = std::count_if(values, values + 5, std::not1(IsOdd()));
    //计算不为奇数的个数
    std::cout << "There are " << cx << " elements with even values.\n";
    return 0;
}

not2 示例:

#include <algorithm>
#include <functional>
#include <iostream>
using namespace std;
int main() {
    std::vector<int> nums = {5, 3, 4, 9, 1, 7, 6, 2, 8};
    // 升序
    std::function<bool(int, int)> ascendingOrder = [](int a, int b) {
        return a < b;
    };
    // 排序,不是按升序,而是按降序
    std::sort(nums.begin(), nums.end(), std::not2(ascendingOrder));
    for (int i : nums) {
        std::cout << i << " ";
    }
    return 0;
}

# 成员函数适配器

mem_fun_ref 的作用和用法跟 mem_fun 一样,将成员函数变成函数对象

唯一的不同就是:当容器中存放的是对象实体的时候用 mem_fun_ref,当容器中存放的是对象的指针的时候用 mem_fun。

#include <algorithm>  // for_each()
#include <functional>
#include <iostream>
#include <vector>
using namespace std;
class Test {
   public:
    int doSomething() {
        cout << "output from method doSomething" << endl;
        return 1;
    }
};
int doSome(Test *test) { return test->doSomething(); }
// 声明一个使用成员函数的全局函数
int main() {
    vector<Test *> v;
    for (int i = 0; i < 5; ++i) {
        Test *t = new Test();
        v.push_back(t);
    }
    // 若不想调用 doSome() 函数,而是想调用成员函数
    // 就可以使用 mem_fun 将对象的方法转换为函数对象的用法
    for_each(v.begin(), v.end(), mem_fun(&Test::doSomething));
    return 0;
}

# 普通函数适配器

为了配合 bind 使用,普通的函数不可以使用绑定函数适配器,所以使用 ptr_fun 作为 bind 的参数,实现普通函数用法向函数对象用法的转换

#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
bool g(int x, int y) { return x > y; }
int main() {
    int a[] = {2, 5, 3, 7, 1, 9, 8, 0, 6};
    int nSize = sizeof(a) / sizeof(int);
    int nCount = count_if(a, a + nSize, bind2nd(ptr_fun(g), 3));
    // 这里不能写 bind2nd(g, 3)
    cout << nCount;
    return 0;
}