当前位置: 首页 > news >正文

《STL源码剖析》理解之将类成员函数和for_each等算法结合

类成员函数可以通过函数适配器(function adapters)包装成一个仿函数(重载了operator()的类),将其搭配于STL算法一起使用。

#include <algorithm>
#include <functional>
#include <vector>
#include <iostream>using namespace std;class Int
{
public:explicit Int(int i) : m_i(i) {}void print1() const { cout << '[' << m_i << ']'; }void print2(int i) const { cout << '[' << i << ']'; }
private:int m_i;
};int main()
{Int t1(3), t2(7), t3(20), t4(14), t5(68);vector‹Int> Iv;Iv.push_back(t1);Iv.push_back(t2);Iv.push_back(t3);Iv.push_back(t4);Iv.push_back(t5);for_each(Iv.begin(), Iv.end(), mem_fun_ref(&Int::print1));    //okfor_each(Iv.begin(), Iv.end(), mem_fun_ref(&Int::print2));    //err!!}

为何上述代码Int::print1可以搭配for_each使用而Int::print2却不行?我用vs2015编译上述语句出错时跟随所在err可以看到for_each的源代码:

template<class _InIt,class _Fn1> inlinevoid _For_each_unchecked(_InIt _First, _InIt _Last, _Fn1& _Func){    // perform function for each elementfor (; _First != _Last; ++_First)_Func(*_First);}

可知所调用的func的参数是迭代器解引用的对象,该对象类型和这里vector内value_type(元素类型)一样。显然print2的参数是int而不是Int。那么我把print2的参数改为Int是不是就可以了呢?

using namespace std;class Int
{...int get() { return m_i;  }void print2(Int i) const { cout << '[' << i.get() << ']'; }
};...for_each(Iv.begin(), Iv.end(), mem_fun_ref(&Int::print2));    //err!!

结果还是不行,编译报错“ c++ error 2064: term does not evaluate to a function taking 1 arguments”。既然抱怨的是“_Func这个函数不能转为接受一个参数的函数”,但以上代码使用的函数“print2”明明是只接受一个参数的函数,那为什么编译器还会报这个错呢?是因为在这个例子中,mem_fun_ref返回一个仿函数,而这个仿函数接受两个参数,一个是T*指针(也就是方法所在类对象的指针,可理解为this),第二个才是我们需要的Int。所以编译器会产生这个错误。

mem_fun_ref作为一个函数模板,内部返回了一个类模板对象mem_fun_ref_t,大概长这个样子:

template <class S, class T>
inline mem_fun_ref<S, T> mem_fun(S (T::*f)()){ return mem_fun_ref_t<S,T>(f); }template <class S, class T>
class mem_fun_ref_t : public unary_function<T, S> {
public:explicit mem_fun_ref_t(S (T::*pf)()) : f(pf) {}S operator()(T& r) const { return (r.*f)(); }
private:S (T::*f)();
};

mem_fun_ref_t其实是unary_function仿函数的适配器。反正它通过模板参数匹配的手法在mem_fun_ref_t内部推导出了了类的类型T,类成员函数的返回类型S,还存了这个成员函数的指针,一旦调用即operator()通过r.*f进行函数调用。注意f是指针,r.*f表示对f进行解引用(不是*(r.f)噢,这种写法虽然有点奇特)。

回归正题,解决上述这个无法将一个参数以上的成员函数应用于算法的问题,有2种方法:

  1. 将函数print2改为一个静态方法,这样print2就无需this指针就可调用,和一个普通的函数指针无异,因此直接使用&Int::print2即可:

class Int {
public:explicit Int(int i) : m_i(i) {}static void print2(Int obj) { cout << '[' << obj.get() << ']'; }int get() { return m_i;  }
private:int m_i;
};for_each(Iv.begin(), Iv.end(), &Int::print2);
  1. 使用bind,将将接受2个参数的print2绑定为真正接受一个参数Int的函数对象:

class Int {
public:explicit Int(int i) : m_i(i) {}void print2(Int obj) { cout << '[' << obj.get() << ']'; }int get() { return m_i;  }
private:int m_i;
};Int tmp(1);
for_each(Iv.begin(), Iv.end(), std::bind(&Int::print2, tmp, std::placeholders::_1));

以上。

http://www.lryc.cn/news/20452.html

相关文章:

  • 如何构建应用标准化体系
  • 【RabbitMQ笔记03】消息队列RabbitMQ七种模式之WorkQueues工作队列模式
  • 认识html
  • 在外包公司熬了 3 年终于进了字节,竭尽全力....
  • 绝对让你明明白白,脚把脚带你盯着 I2C 时序图将 I2C 程序给扣出来(基于STM32的模拟I2C)
  • 2023年全国最新工会考试精选真题及答案5
  • 一文2000字手把手教你自动化测试Selenium+pytest+数据驱动
  • windows安装Ubuntu子系统以及图形化界面记录
  • 通俗易懂,十分钟读懂DES,详解DES加密算法原理,DES攻击手段以及3DES原理。Python DES实现源码
  • 为多态基类声明virtual析构函数
  • 啊哈 算法读书笔记 第 2 章 栈、队列、链表
  • Git ---- IDEA 集成 Git
  • 【LeetCode 704】【Go】二分查找
  • 【代码随想录训练营】【Day23】第六章|二叉树|669. 修剪二叉搜索树 |108.将有序数组转换为二叉搜索树|538.把二叉搜索树转换为累加树
  • CV——day78 读论文:通过静态背景构建扩展低通道路边雷达的探测距离(目标是规避风险)
  • 【编程入门】应用市场(go语言版)
  • Linux(openEuler)没有界面连接互联网方法
  • 第一天 软考中级--嵌入式系统设计师考试复习教程开始了
  • 分享 10 个高频 Python 面试题
  • ThreadLocal原理、结构、源码解析
  • 分布式之PBFT算法
  • Linux 操作系统——查看/修改系统时区、时间、本地时间修改为UTC
  • CSS数据类型以及符号
  • LeetCode-54. 螺旋矩阵
  • 【Python入门第十八天】Python For 循环
  • Qt图片定时滚动播放器
  • 李宏毅2023春季机器学习课程
  • 计算机操作系统知识点汇总
  • 【离线数仓-8-数据仓库开发DWD层设计要点-交易域相关事实表】
  • 计算机网络(七):DNS协议和原理,DNS为什么用UDP,网页解析的全过程