ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

stl使用中的经验(十四)--ptr_fun、mem_fun、mem_fun_ref

2021-11-28 20:01:01  阅读:186  来源: 互联网

标签:__ 函数 mem each stl vw fun


首先我们看个例子。

#include <iostream>
#include <vector> 
#include <algorithm> 
#include <iterator>
using namespace std;

class Widget{
	public:
	Widget(int a) : m_a(a)
	{
	}
	
	int value() const
	{
		return m_a;
	}
	
	bool test()
	{
		return m_a % 3;
	}
	
private:
	int m_a;
};

bool test(const Widget& w)
{
	return w.value() % 3;
}

int main()
{
	typedef vector<Widget> WdgVec;
	typedef vector<Widget*> WdgPtrVec;
	typedef vector<Widget>::iterator WdgIter;
	typedef vector<Widget*>::iterator WdgPtrIter;
	
	WdgVec vw;
	WdgPtrVec vwp;
	
	for(int index = 0; index < 10; ++index)
	{
		Widget w(index);
		vw.push_back(w);
		
		vwp.push_back(new Widget(index));
	}
	
	for_each(vw.begin(), vw.end(), test);
	for_each(vw.begin(), vw.end(), &Widget::test);
	for_each(vwp.begin(), vwp.end(), &Widget::test);
	return 0;
}

通过编译运行上面的例子,我们能够得知,其中:

for_each(vw.begin(), vw.end(), &Widget::test);
for_each(vwp.begin(), vwp.end(), &Widget::test);

这两行代码都是编译不通过的。编译报错:

[Error] must use '.*' or '->*' to call pointer-to-member function in '__f (...)', e.g. '(... ->* __f) (...)'

为什么呢?

按照正常理解,我们传给了for_each算法一个函数地址,应该是能够正常编译通过的。

如果有一个函数f()和一个对象x,假设我们要在对象x上调用函数,通常是有三种语法来进行这个调用。

  1. 如果函数f()不是对象x的成员函数,那么我们直接调用 f(x)
  2. 如果函数f()是对象x的成员函数,并且x是一个对象或者一个对象的引用,则 x.f()
  3. 如果函数f()是对象x的成员函数,并且p是一个指向对象x的指针,则 p->f()

在上面的例子中,我们创建了两个容器,vwvwp,一个存放对象Widget,一个存放指向对象Widget的指针。

当函数test()不是对象Widget的成员函数时,我们直接调用了函数。并且编译正常。那么在理想情况下,当函数test()是对象的成员函数时,我们应该也是能够通过&Widget::test来调用它。

其实,在上面的三次调用中,分别对应了我们前面提到的C++提供的三种语法的调用方式。但是for_each算法是只有一个的,我们看下for_each的实现。

template <class _InputIter, class _Function>
_Function for_each(_InputIter __first, _InputIter __last, _Function __f) {
  __STL_REQUIRES(_InputIter, _InputIterator);
  for ( ; __first != __last; ++__first)
    __f(*__first);
  return __f;
}

通过上面for_each的实现我们可以很明显的看出来,for_each的实现是基于我们前面所说的语法一来进行对函数调用的。这也是stl中一种普遍的惯例:函数或者函数对象被调用时,总是使用非成员函数的语法形式。

这也就证明了上面我们的后两次调用为什么编译不通过的。

至此,或许mem_funmem_fun_ref必须存在的意义已经很明显了,也就是他们用来调整(后两种调用方式)成员函数,能够被通过非成员函数的方式调用。

mem_funmem_fun_ref是函数模版,实现方式有多种,我们看一下其中一种。

template <class _Ret, class _Tp>
inline mem_fun_t<_Ret,_Tp> mem_fun(_Ret (_Tp::*__f)())
  { return mem_fun_t<_Ret,_Tp>(__f); }
template <class _Ret, class _Tp>
class mem_fun_t : public unary_function<_Tp*,_Ret> {
public:
  explicit mem_fun_t(_Ret (_Tp::*__pf)()) : _M_f(__pf) {}
  _Ret operator()(_Tp* __p) const { return (__p->*_M_f)(); }
private:
  _Ret (_Tp::*_M_f)();
};
mem_fun(_Ret (_Tp::*__f)())

带一个指向某个成员函数的指针参数__f,并且返回一个 mem_fun_t 的对象。

mem_fun_t是一个函数子类,他拥有该成员函数的指针,并提供了operator()函数,在operator()中调用了通过参数传递进来的对象的成员函数。

所以我们可以修改前面例子中的代码。

for_each(vw.begin(), vw.end(), mem_fun_ref(&Widget::test));
for_each(vwp.begin(), vwp.end(), mem_fun(&Widget::test));

来正常的调用成员函数。

我们的第一个调用。

for_each(vw.begin(), vw.end(), test);

传给for_each的是一个真正的函数,所以,我们就不必要对for_each进行语法调整。

前面我们也提到了ptr_fun,下面我们先看下ptr_fun的实现。

template <class _Arg, class _Result>
inline pointer_to_unary_function<_Arg, _Result> ptr_fun(_Result (*__x)(_Arg))
{
  return pointer_to_unary_function<_Arg, _Result>(__x);
}

如果你已经度过前面的文章的话,你或许应该已经知道了,ptr_fun提供了给函数加上类型定义的功能。

比如在stl中的四个标准的配接器中,必须要使用类型定义。

比如我们前面的调用

for_each(vw.begin(), vw.end(), test);

但是如果当我们使用下面的调用的时候,就需要使用ptr_fun来进行类型定义的设置。

for_each(vw.begin(), vw.end(), not1(test));

其实很多时候如果你并不知道该不该加ptr_fun的时候,加上总是没有错的,也不会影响算法的性能,唯一的不好只是在别人阅读你的代码的时候会造成一些障碍

也可以在任何地方都不加,当编译器报错提醒你的时候再返回来加进去就行

但是mem_funmem_fun_ref是当调用成员函数的时候必须要加的,因为在调用的时候,并不是只引入了类型定义,而且还进行了语法调用形式的转换来适应算法。

标签:__,函数,mem,each,stl,vw,fun
来源: https://blog.csdn.net/tax10240809163com/article/details/121596318

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有