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;
    }
};

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

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

相关推荐
怀澈12221 分钟前
高性能服务器模型之Reactor(单线程版本)
linux·服务器·网络·c++
chnming198744 分钟前
STL关联式容器之set
开发语言·c++
威桑1 小时前
MinGW 与 MSVC 的区别与联系及相关特性分析
c++·mingw·msvc
熬夜学编程的小王1 小时前
【C++篇】深度解析 C++ List 容器:底层设计与实现揭秘
开发语言·数据结构·c++·stl·list
yigan_Eins1 小时前
【数论】莫比乌斯函数及其反演
c++·经验分享·算法
Mr.131 小时前
什么是 C++ 中的初始化列表?它的作用是什么?初始化列表和在构造函数体内赋值有什么区别?
开发语言·c++
阿史大杯茶1 小时前
AtCoder Beginner Contest 381(ABCDEF 题)视频讲解
数据结构·c++·算法
C++忠实粉丝1 小时前
计算机网络socket编程(3)_UDP网络编程实现简单聊天室
linux·网络·c++·网络协议·计算机网络·udp
我们的五年2 小时前
【Linux课程学习】:进程描述---PCB(Process Control Block)
linux·运维·c++
程序猿阿伟2 小时前
《C++ 实现区块链:区块时间戳的存储与验证机制解析》
开发语言·c++·区块链