STL源码剖析:仿函数

什么是仿函数(函数对象)?为什么不直接用函数指针?

可以当函数使用的对象,一个类或结构体,重载圆括号操作符 "()",可以像函数一样被调用。仿函数对象可以当成参数传入函数中。

class/struct Obj{
  operator()(args){...}
};
Obj obj;
//方法1
obj(args);
//方法2
Obj()(args);

为什么重载()运算符可以调用?C++的规定

STL(标准模板库)中,使用仿函数而不是简单的函数指针:

  1. **封装性:**仿函数可以封装状态,它们可以拥有数据成员。仿函数可以在多次调用之间保持一些内部状态。函数指针只能指向一个独立的函数,这个函数没有自己的状态。
  2. **灵活性:**由于仿函数是一个类,可以面向对象编程的特性。比如可以定义多个仿函数类来完成不同的任务,或者通过继承来扩展已有的仿函数类,这样可以更容易地组合和复用代码。
  3. STL中的许多组件(如容器、算法和迭代器)都是设计来协同工作的。仿函数可以更好地融入这种设计模式,尤其是当涉及到适配器(adapters)和其他高级特性时。仿函数可以被设计成STL组件的一部分,使得它们可以轻松地与其他STL组件交互。(需要理解)

比较大小的仿函数std::greater和std::less

cpp 复制代码
template<typename _Tp>
struct greater{
   bool operator()(const _Tp& __x, const _Tp& __y) const
   { return __x > __y; }
};
template<typename _Tp>
struct less{
     bool operator()(const _Tp& __x, const _Tp& __y) const
      {return __x < __y;}
 };
cpp 复制代码
 greater<int>gt;
 cout<<gt(2,3); //0

STL 仿函数的分类,若以操作数(operand)的个数划分,可分为一元和二元仿函数,若以功能划分,可分为算术运算(Arithmetic)、关系运算(Rational)、逻辑运算(Logical)三大类。任何应用程序欲使用 STL 内建的仿函数,都必须包含 <functional> 头文件,SGI 则将它们实际定义于<stl_function.h>文件中。

可配接(Adaptable)的关键

仿函数的相应型别主要用来表现函数参数型别和传回值型别。为了方便起见,<stl_function.h>定义了两个 classes,分别代表一元仿函数和二元仿函数(STL不支持三元仿函数),其中没有任何数据成员和成员函数只有一些型别定义。

任何仿函数,只要选择继承其中一个 class,就拥有那些相应型别,也就自动拥有了配接能力。

一元仿函数:unary_function

unary_function用来呈现一元函数的参数型别和回返值型别。其定义非常简单:

cpp 复制代码
// STL规定,每一个 Adaptable Unary Function 都应该继承此类别。
template <class Arg,class Result>
struct unary_function {
  typedef Arg argument_type; //表示仿函数的参数类型.
  typedef Result result_type;//表示仿函数的返回类型.
}
cpp 复制代码
template <classT>structnegate : public unary_function<T, T> {
    T operator()(const T& x)const{ return -x; }
};

negate继承了unary_function<T, T>,说明它的参数类型和返回类型都是 Toperator() 是重载的函数调用运算符,使得这个结构体可以像函数一样被调用。它接受一个参数并返回其负值。

仿函数适配器unary_negate

cpp 复制代码
template <class Predicate>
class unary_negate {
public:
    bool operator()(const typename Predicate::argument_type& x) const {
        // 逻辑负值操作
    }
};

通过组合已有的仿函数来创建新的行为。在这种情况下,unary_negate 将一个仿函数的逻辑输出取反。这种方式可以避免重复编写逻辑负值的代码,而是通过组合来实现新功能。

二元仿函数:binary_function
cpp 复制代码
template <class Arg1, class Arg2, class Result>
struct binary_function {
    typedef Arg1 first_argument_type;//二元仿函数的第一个参数类型
    typedef Arg2 second_argument_type;//二元仿函数的第二个参数类型
    typedef Result result_type;//二元仿函数的返回值类型
};
cpp 复制代码
template<class T>
struct plus : public binary_function<T, T, T> {
     T operator()(const T& x, const T& y) const { return x + y; }
};

plus继承了binary_function<T, T, T>,表示两个参数和返回值都是类型 Toperator() 是重载的函数调用运算符,使得 plus仿函数可以像普通函数一样使用,它接受两个参数并返回它们的和。

仿函数适配器 binder1st

cpp 复制代码
template <class Operation>
class binder1st {
protected:
    Operation op;
    typename Operation::first_argument_type value;
public:
    typename Operation::result_type
    operator()(const typename Operation::second_argument_type& x) const {
        return op(value, x);
    }
};

binder1st 适配器通过将二元仿函数的第一个参数绑定为固定值,将其转换为一元仿函数。

算术类仿函数

STL 内建的"算术类仿函数",支持加法、减法、乘法、除法、模数(余数,modulus)和否定(negation)运算。除了"否定"运算为一元运算,其它都是二元运算。

加法(plus<T>),减法(minus<T>),乘法(multiplies<T>),除法(divides<T>),模取(modulus<T>),否定(negate<T>)

cpp 复制代码
template <class T>
struct plus : public binary_function<T, T, T> {
   T operator()(const T& x, const T& y) const { return x + y; }
};

template <class T>
struct minus : public binary_function<T, T, T> {
    T operator()(const T& x, const T& y) const { return x - y; }
};

template <class T>
struct multiplies : public binary_function<T, T, T> {
    T operator()(const T& x, const T& y) const { return x * y; }
};

template <class T>
struct divides : public binary_function<T, T, T> {
    T operator()(const T& x, const T& y) const { return x / y; }
};

template <class T>
struct modulus : public binary_function<T, T, T> {
    T operator()(const T& x, const T& y) const { return x % y; }
};

template <class T>
struct negate : public unary_function<T, T> {
    T operator()(const T& x) const { return -x; }
};
关系运算类仿函数

STL 内建的"关系运算类仿函数"支持了等于、不等于、大于、大于等于、小于、小于等于六种运算。每一个都是二元运算。

等于(equal_to<T>),不等于(not_equal_to<T>),大于(greater<T>),大于或等于(greater_equal<T>),小于less<T>,小于或等于less_equal<T>。

cpp 复制代码
template <class T>
struct equal_to : public binary_function<T, T, bool> {
    bool operator()(const T& x, const T& y) const { return x == y; }
};

template <class T>
struct not_equal_to : public binary_function<T, T, bool> {
    bool operator()(const T& x, const T& y) const { return x != y; }
};

template <class T>
struct greater : public binary_function<T, T, bool> {
    bool operator()(const T& x, const T& y) const { return x > y; }
};

template <class T>
struct less : public binary_function<T, T, bool> {
    bool operator()(const T& x, const T& y) const { return x < y; }
};

template <class T>
struct greater_equal : public binary_function<T, T, bool> {
    bool operator()(const T& x, const T& y) const { return x >= y; }
};
template <class T>
struct less_equal : public binary_function<T, T, bool> {
    bool operator()(const T& x, const T& y) const { return x <= y; }
};
证同(identity)、选择(select)、投射(project)

identity****仿函数

cpp 复制代码
struct identity : public unary_function<T, T> {
    const T& operator()(const T& x) const { return x; }
};

identity 仿函数直接返回其输入参数,不做任何修改。

select1st****仿函数

cpp 复制代码
template <class Pair>
struct select1st : public unary_function<Pair, typename Pair::first_type> {
    const typename Pair::first_type& operator()(const Pair& x) const {
        return x.first;
    }
};

接受一个 pair,并返回其第一个元素(first)。

select2nd****仿函数

cpp 复制代码
template <class Pair>
struct select2nd : public unary_function<Pair, typename Pair::second_type> {
    const typename Pair::second_type& operator()(const Pair& x) const {
        return x.second;
    }
};

project1st****仿函数

cpp 复制代码
template <class Arg1, class Arg2>
struct project1st : public binary_function<Arg1, Arg2, Arg1> {
    Arg1 operator()(const Arg1& x, const Arg2&) const { return x; }
};

接受两个参数,只返回第一个参数,忽略第二个参数。

project2nd****仿函数

cpp 复制代码
template <class Pair>
struct select2nd : public unary_function<Pair, typename Pair::second_type> {
    const typename Pair::second_type& operator()(const Pair& x) const {
        return x.second;
    }
};

接受两个参数,只返回第二个参数,忽略第一个参数。

这些仿函数在泛型编程中提供了抽象化和间接性。

相关推荐
红龙创客2 分钟前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++
Lenyiin4 分钟前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin
yuanbenshidiaos2 小时前
c++---------数据类型
java·jvm·c++
十年一梦实验室2 小时前
【C++】sophus : sim_details.hpp 实现了矩阵函数 W、其导数,以及其逆 (十七)
开发语言·c++·线性代数·矩阵
taoyong0012 小时前
代码随想录算法训练营第十一天-239.滑动窗口最大值
c++·算法
这是我582 小时前
C++打小怪游戏
c++·其他·游戏·visual studio·小怪·大型·怪物
fpcc2 小时前
跟我学c++中级篇——C++中的缓存利用
c++·缓存
呆萌很3 小时前
C++ 集合 list 使用
c++
诚丞成4 小时前
计算世界之安生:C++继承的文水和智慧(上)
开发语言·c++
东风吹柳4 小时前
观察者模式(sigslot in C++)
c++·观察者模式·信号槽·sigslot