欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

stl 函数适配器之std::mem_fun(c++98),sd::mem_fun_ref(C++98), std::men_fn(C++11)的用法

程序员文章站 2022-05-31 21:45:05
...

1. 环境

Qt 5.12.4 MinGW 64-bit

2. std::mem_fun(deprecated in C++11)

2.1 定义

指针版本:通过指向对象的指针调用成员函数时使用

//针对无参数(this除外)的非const成员函数,返回值可选。
template<typename _Ret, typename _Tp>
inline mem_fun_t<_Ret, _Tp> mem_fun(_Ret (_Tp::*__f)())
{ return mem_fun_t<_Ret, _Tp>(__f); }

//针对无参数(this除外)的const成员函数,返回值可选。
template<typename _Ret, typename _Tp>
inline const_mem_fun_t<_Ret, _Tp> mem_fun(_Ret (_Tp::*__f)() const)
{ return const_mem_fun_t<_Ret, _Tp>(__f); }

//针对带一个参数(this除外)的非const成员函数,返回值可选。
template<typename _Ret, typename _Tp, typename _Arg>
inline mem_fun1_t<_Ret, _Tp, _Arg> mem_fun(_Ret (_Tp::*__f)(_Arg))
{ return mem_fun1_t<_Ret, _Tp, _Arg>(__f); }

//针对非const带一个参数(this除外)的成员函数,返回值可选。
template<typename _Ret, typename _Tp, typename _Arg>
inline const_mem_fun1_t<_Ret, _Tp, _Arg> mem_fun(_Ret (_Tp::*__f)(_Arg) const)
{ return const_mem_fun1_t<_Ret, _Tp, _Arg>(__f); }

std::mem_fun是一个函数模板,有四个重载版本,分别使用于不同的情况(参见注释)。

2.2 作用

将成员函数转换为函数对象(指针版本)。指针版本指的是通过指向对象的指针调用对象的情况。std::mem_fun_ref是对应的引用版本。

std::mem_fun返回一个函数对象,该对象封装了类型_Tp的成员函数,成员函数可以有返回值(可选),至多带一个参数(this除外)。

std::mem_fun返回的函数对象需要一个指向对象的指针作为 其operator()运算符的第一个参数。

std::mem_fun_ref返回的函数对象需要一个指向对象的引用作为其operator()运算符的第一个参数。

std::mem_fun的作用是将成员函数转换为函数对象,为什么需要这样的转换呢?或者说什么情况下需要做这样的转换了?

答案是在算法中需要使用类的成员函数时。当然这是最主要/最重要的用法,而不是唯一用法。

那为什么不能直接向算法中传递一个成员函数指针?大多数算法接受的参数是一个可调用对象,而成员函数指针不属于可调用对象。这就是std::mem_fun存在的最重要的意义。

2.3 用法

我们定义一个Student类,该类非常简单:

class Student
{
public:
   Student(int inScore, std::string inName);
   bool isLess(const Student &rhs);
   void PrintInfo();
   void additionalScore(int addScore);
private:
   int score;
   std::string Name;
};

Student::Student(int inScore, string inName)
   :score(inScore)
   ,Name(inName)
{ }

bool Student::isLess(const Student &rhs)
{
   return  this->score < rhs.score;
}

void Student::PrintInfo()
{
   std::cout << Name << ": " << score << std::endl;
}

void Student::additionalScore(int addScore)
{
   score +=addScore;
}

现在我们用一个vector来保存一组学生的信息:

std::vector<Student*> pStudents;//注意:vector存的是指针。
pStudents.reserve(5);
pStudents.push_back(new Student(78, "xiaoming"));
pStudents.push_back(new Student(66, "xiaozhang"));
pStudents.push_back(new Student(80, "xiaohong"));
pStudents.push_back(new Student(100, "xiaoli"));
pStudents.push_back(new Student(88, "xiaotang"));

现在有一个要求:调用Student::PrintInfo()成员函数打印所有学生的信息(要求使用算法std::for_each)。
您可能会这样写代码:

std::for_each(pStudents.begin(),pStudents.end(), &Student::PrintInfo);

很遗憾,这样的代码不能通过编译。至于原因? 我们先来看看std::fot_each的实现:

template<typename _InputIterator, typename _Function>
_Function for_each(_InputIterator __first, _InputIterator __last, _Function __f)
 {
   for (; __first != __last; ++__first)
		__f(*__first);//原因在这里:__f必须是可调用的。而成员函数是不可调用的。
   return __f; 
 }

成员函数指针与普通函数指针不同,它必须绑定到特定的对象上才能使用,所以成员函数指针不是一个可调用对象。

既然找到了原因,那么现在我们就需要一种方式将成员函数指针转换为一个可调用的对象:没错,就是std::mem_fun的作用是将成员函数转换为函数对象,函数对象是可调用的。
so, 正对上面的要求,您可以书写这样的代码:

//std::for_each(pStudents.begin(),pStudents.end(), &Student::PrintInfo);//error
std::for_each(pStudents.begin(),pStudents.end(), std::mem_fun(&Student::PrintInfo));//ok!

不知道大家发现没有,Student::PrintInfo()是不带任何参数的(this除外)。那么如果我们需要调用的成员函数待带参数呢?
出于某种原因,我们需要给每个学生在原先的分数上添加一个附加分,这需要调用成员函数Student::additionalScore来实现,并要求使用算法std::for_each:

std::for_each(pStudents.begin(), pStudents.end(), std::bind2nd(std::mem_fun(&Student::additionalScore), 10));

遗憾的是std::mem_fun至多支持1个参数(this除外)的成员函数。对于更多的参数的成员函数std::mem_fun就无能为力。

3 std::mem_fun_ref(deprecated in C++11)

3.1 定义

引用版本:通过对象调用成员函数时使用

//针对无参数(this除外)的非const成员函数,返回值可选。
template<typename _Ret, typename _Tp>
inline mem_fun_ref_t<_Ret, _Tp> mem_fun_ref(_Ret (_Tp::*__f)())
{ return mem_fun_ref_t<_Ret, _Tp>(__f); }

//针对无参数(this除外)的const成员函数,返回值可选。
template<typename _Ret, typename _Tp>
inline const_mem_fun_ref_t<_Ret, _Tp> mem_fun_ref(_Ret (_Tp::*__f)() const)
{ return const_mem_fun_ref_t<_Ret, _Tp>(__f); }

//针对带一个参数(this除外)的非const成员函数,返回值可选。
template<typename _Ret, typename _Tp, typename _Arg>
inline mem_fun1_ref_t<_Ret, _Tp, _Arg> mem_fun_ref(_Ret (_Tp::*__f)(_Arg))
{ return mem_fun1_ref_t<_Ret, _Tp, _Arg>(__f); }

//针对带一个参数(this除外)的非const成员函数,返回值可选。
template<typename _Ret, typename _Tp, typename _Arg>
inline const_mem_fun1_ref_t<_Ret, _Tp, _Arg> mem_fun_ref(_Ret (_Tp::*__f)(_Arg) const)
{ return const_mem_fun1_ref_t<_Ret, _Tp, _Arg>(__f); }

针对上面的例子,如果我们的vector存的是Student对象本身,而不是指向Student对象的指针:

std::vector<Student> students;
students.reserve(5);
students.push_back(Student(78, "xiaoming"));
students.push_back(Student(66, "xiaozhang"));
students.push_back(Student(80, "xiaohong"));
students.push_back(Student(100, "xiaoli"));
students.push_back(Student(88, "xiaotang"));

对于:调用Student::PrintInfo()成员函数打印所有学生的信息(要求使用算法std::for_each),我们的代码应该是这样的:

std::for_each(students.begin(), students.end(), std::mem_fun_ref(&Student::PrintInfo));

如果需要调用Student::additionalScore,我们需要这样写:

std::for_each(students.begin(), students.end(), 
std::bind2nd(std::mem_fun_ref(&Student::additionalScore), 10) );

4 std::mem_fn(since C++11)

4 优缺点比较

  1. std::mem_fun,std::mem_fun_ref至多支持一个参数(this除外)的成员函数。
  2. 语法太多了,对于通过对象调用成员函数和通过指向对象的指针调用成员函数有不同的语法。

5 结语

std::mem_fun和std::mem_fun_ref的作用都是将成员函数转换为函数对象,以便可以在算法中使用。

std::mem_fun在通过 指向对象的指针 调用成员函数的情况下使用。

std::mem_fun_ref在通过 对象本身 调用成员函数的情况下使用。

std::mem_fun的用法不仅只有上面介绍的,这里只总结了最常用的用法。不过其本质是封装指向成员函数的指针,返回一个函数对象。