C++中的仿函数(functors)和仿函数适配器(adapter function)

1. C++标准库中的仿函数例子

比如下面的plus类, 重载了()运算符, 可以这样子用plus p; p(1, 2);, 类可以像调用函数一样的行为, 被称为仿函数.
在这里插入图片描述
再看看多几个例子:
在这里插入图片描述
然后我们自己写一个仿函数, 然后用sort调用试试:

struct myclass
{
    bool operator()(int i, int j) { return i < j; }

} myobj;//声明一个变量
void test01()
{
    vector<int> v{1,3,4,2,5,2};
    sort(v.begin(), v.end(), myobj);
    for(auto e: v){
        cout << e << " ";
    }
    cout << endl;
}

输出是

1 2 2 3 4 5

但是这里有一个问题,
有没有发现标准库的仿函数都需要继承某个类, 这某个类是下面其中一种:
unary_function类或者binary_function类.
而我们的做法没有继承, 这样的不好之处是没有融入到标准库STL中
.
那为什么会是不好的呢?

2. 仿函数的可适配(adaptable)条件

如下图, 要知道STL中是存在多种适配器的, 下图中包含

  1. 迭代器适配器
  2. 容器适配器
  3. 仿函数适配器

我们这里说仿函数适配器. 那仿函数适配器是干嘛的呢?
仿函数适配器向仿函数提问, 然后仿函数进行回答.
这个功能主要是这样实现的:

template<typename _Arg, typename _Result>
struct unary_function
{
  typedef _Arg argument_type;
  typedef _Result result_type;
};

template<typename _Arg1, typename _Arg2, typename _Result>
struct binary_function
{
 typedef _Arg1 first_argument_type;
 typedef _Arg2 second_argument_type;
 typedef _Result result_type;
};

也就是把仿函数的第一个参数, (第二个参数)和返回值类型额外命了名, 方便统一调用.
在这里插入图片描述
下面是less的例子:
因为less仿函数有两个参数, 因此继承时需要继承binary_function.
在这里插入图片描述

前面的博客里写了容器适配器, 容器适配器一共额外命名了5个类型, 如下图:
举例stack来说, 它内含deque作为底层容器, 同时需要额外命名5个类型, 这样我们在其他地方用到的时候, 就会很方便(具体例子后面会说)

在这里插入图片描述


让我们继续回到函数适配器中,分析一下函数适配器的具体例子:

count_if(v1.begin(), v1.end(), 
		not1(bind2nd(less<int>(), 40)));
  1. 首先我们知道less<int>()这样是一个对象, 当要调用它时, 其参数是需要两个的;
  2. 后面是一个参数40,
  3. 这两个参数分别作为bind2nd的两个参数, 看下图左上角可知, 我们传入的两个参数会被模板函数自动推断出来(其实bind2nd在这里只是一个辅助函数, 功能就是把类型自动推断出来), 那我们就知道其中op = less<int>(), x = 40了, 然后返回的话就创建一个binder2nd对象. 需要注意的是: 因为x要作为op的第二个参数, 因此返回的时候用arg2_type(x)将x转为arg2_type这种类型了.
  4. 现在我们就到了binder2nd类中了, 先走到构造函数中, 类把x和y分别保存为opvalue, op为操作, value为第二个参数, 然后我们就等待
  5. (先忽略not1)我们现在调用count_if, 传入的参数分别是v1.begin(), v1.end(), 和bind2nd()(这时候bind2nd是一个对象, 但是同时它可以像函数一样被调用), 然后执行到pred(*first)这里, 其实就是执行binder2nd中的()重载函数, 很显然我们看到, 传入的参数只有一个, 就是*first, 然后执行的是less<int>()(x, value) 进行比较, 返回结果

以上这种利用辅助函数来推导类型, 然后函数接着调用另一个类, 类中保存操作方式和一个操作数, 此时还没有调用, 也就还没有绑定, 然后执行算法的时候, 再调用()重载进行调用 的行为, 就被称为函数适配器. (个人理解)

在这里插入图片描述


接下来我们说一下not1
其实not1和上面的差不多, 我们接着上面的来: bind2nd之后,

not1(bind2nd...);
  1. 我们首先要知道, bind2nd是一个对象, 因此相当于not1(对象), 这个时候我们就进入not1辅助函数(下图左上角), 返回调用unary_negate类创建对象, 要知道传入的参数是pred, 也就是对象
  2. 现在来到unary_negate类, 继承的是unary_function, 因为需要一个参数, 所以不用binary_function. 接着构造函数将这个对象保存下来
  3. 执行count_if函数, 要知道我们传入的也是一个对象, 一个类名为unar_negate的对象. 在pred(*first)中, 调用对象的()重载函数操作, 函数内部调用类中保存的pred对象, 然后递归调用binder2nd对象, 最后取反.
    在这里插入图片描述

3. C++11之后出现的新型适配器:bind

以下个人觉得用起来比较麻烦, 仅列举:
在这里插入图片描述
用法例子:
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zedjay_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值