C++11中的std::mem_fn怎么用

这篇“C++11中的std::mem_fn怎么用”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“C++11中的std::mem_fn怎么用”文章吧。

成都创新互联公司服务项目包括元宝网站建设、元宝网站制作、元宝网页制作以及元宝网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,元宝网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到元宝省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!

1、源码准备

本文是基于gcc-4.9.0的源代码进行分析,std::mem_fn是C++11才加入标准的,所以低版本的gcc源码是没有std::mem_fn的,建议选择4.9.0或更新的版本去学习,不同版本的gcc源码差异应该不小,但是原理和设计思想的一样的。

2、通过一个简单的例子来了解std::mem_fn的作用

算法是C++标准库中非常重要的组成部分,C++通过算法+容器的方式将数据结构和算法进行了分离,这样可以使用户编写代码的时候获得最大限度的灵活性。假设我们有如下类:

class Age
{
public:
    Age(int v)
        :m_age(v)
    {
    }

    bool compare(const Age& t) const
    {
        return m_age < t.m_age;
    }

    int m_age;
};

我们可以非常方便地使用vector来保存Age对象,如下:

std::vector ages{1, 7, 19, 27, 39, 16, 13, 18};

然后非常方便的利用排序算法进行排序

std::sort(ages.begin(), ages.end(), compare);

代码中的compare是额外定义的一个比较函数,通过这个函数来选择比较的对象并决定比较的结果

bool compare(const Age& t1, const Age& t2)
{
    return t1.compare(t2);
}

严格来讲,算法中要求的并不是函数,而是一个可调用对象。C++中的可调用对象包括函数、函数对象、Lambda表达式、参数绑定等等,它们都可以作为算法的传入参数,但是如果我们按如下来传入参数的话,则会在编译过程中出现错误

std::sort(ages.begin(), ages.end(), &Age::compare);

因为&Age::compare是类成员函数,并非一个可调用对象,如果我们要将它作为比较的参数传递进去的话,就得用std::mem_fn修饰它,如下所示

std::sort(ages.begin(), ages.end(), std::mem_fn(&Age::compare));

从上面的例子可以看到,std::mem_fn的作用就是将类的成员函数转换为一个可调用对象,那么问题来了,std::mem_fn是如何实现这种功能的呢?下面让我们通过分析源码,来揭开std::mem_fn的神秘面纱。

3、std::mem_fn源码解析

3.1、std::mem_fn解析

std::mem_fn位于libstdc++-v3\include\std\functional中

template
inline _Mem_fn<_Tp _Class::*> mem_fn(_Tp _Class::* __pm) noexcept
{
 return _Mem_fn<_Tp _Class::*>(__pm);
}

从代码中可知std::mem_fn是一个模板函数,传入参数为指向_Class类里面的某个成员函数的指针,其返回值为_Tp,而该模板函数返回的值为_Mem_fn<_Tp _Class::*>,接下来看一下_Mem_fn的实现

3.2、std::_Mem_fn解析

std::_Mem_fn位于libstdc++-v3\include\std\functional中

template
class _Mem_fn<_Res (_Class::*)(_ArgTypes...)> : public _Maybe_unary_or_binary_function<_Res, _Class*, _ArgTypes...>
{
    typedef _Res (_Class::*_Functor)(_ArgTypes...);

    template
    _Res _M_call(_Tp&& __object, const volatile _Class *, _Args&&... __args) const
    {
        return (std::forward<_Tp>(__object).*__pmf)(std::forward<_Args>(__args)...);
    }

    template
    _Res _M_call(_Tp&& __ptr, const volatile void *, _Args&&... __args) const
    {
        return ((*__ptr).*__pmf)(std::forward<_Args>(__args)...);
    }

    template
    using _RequireValidArgs = _Require<_AllConvertible<_Pack<_Args...>, _Pack<_ArgTypes...>>>;

    template
    using _RequireValidArgs2 = _Require<_NotSame<_Class, _Tp>, _NotSame<_Class*, _Tp>, _AllConvertible<_Pack<_Args...>, _Pack<_ArgTypes...>>>;

    template
    using _RequireValidArgs3 = _Require, _AllConvertible<_Pack<_Args...>, _Pack<_ArgTypes...>>>;

public:
    typedef _Res result_type;

    explicit _Mem_fn(_Functor __pmf) : __pmf(__pmf) {}

    template>
    _Res operator()(_Class& __object, _Args&&... __args) const
    {
        return (__object.*__pmf)(std::forward<_Args>(__args)...);
    }

    template>
    _Res operator()(_Class&& __object, _Args&&... __args) const
    {
        return (std::move(__object).*__pmf)(std::forward<_Args>(__args)...);
    }

    template>
    _Res operator()(_Class* __object, _Args&&... __args) const
    {
        return (__object->*__pmf)(std::forward<_Args>(__args)...);
    }

    template>
    _Res operator()(_Tp&& __object, _Args&&... __args) const
    {
        return _M_call(std::forward<_Tp>(__object), &__object,
        std::forward<_Args>(__args)...);
    }

    template>
    _Res operator()(reference_wrapper<_Tp> __ref, _Args&&... __args) const
    {
        return operator()(__ref.get(), std::forward<_Args>(__args)...);
    }

private:
    _Functor __pmf;
};

从源代码中可以看出以下几点信息:

  • 该类继承于_Maybe_unary_or_binary_function,由于_Maybe_unary_or_binary_function和本文分析的内容没有太大关联,大家可以自行百度查询其用法,这里就不多作介绍了

  • 类中有一个成员__pmf,其类型是指向上一节传入mem_fn的那个类成员函数的指针,由构造函数初始化

  • 接下来重点看一下类中六个重载的()运算符函数,里面的操作大同小异,基本都是通过__pmf对应的类的对象(多种形式)来调用__pmf成员函数的:

    • 第一个函数_Res operator()(_Class& __object, _Args&&… __args):可以看到,其比原始的类成员函数多要求了一个传入参数,也就是__object,类型是一个类对象的引用,从函数的实现中可以看到原理就是通过这个类对象来直接调用先前那个类成员函数的(没有这个类对象就调用不成立了,因为类成员函数是无法直接调用的,这也是std::mem_fn存在的意义)

    • 第二个函数_Res operator()(_Class&& __object, _Args&&… __args):可以看到该方法第一个传入参数是一个右值引用对象,里面的实现就是通过std::move将对象进行转移而已,其它处理与前面是完全一样的

    • 第三个函数_Res operator()(_Class* __object, _Args&&… __args):可以看到该方法传入了一个对象指针,其它处理与前面是完全一样的

    • 第五个函数_Res operator()(reference_wrapper<_Tp> __ref, _Args&&… __args):可以看到该方法传入了一个被std::reference_wrapper包装的引用,流程和前面的基本一致,比较简单,这里就不多作分析了(关于std::reference_wrapper的问题大家可以看一下这篇文章《C++11的std::ref、std::cref源码解析》,里面有通过源码分析对std::reference_wrapper作出了详细的介绍,这里就不重复说明了)

    • 第四个函数_Res operator()(_Tp&& __object, _Args&&… __args):这个就比较复杂了,这个函数是为了处理传入参数是智能指针或者派生类对象的一个情况的。可以看到函数里调用了_M_call方法,第二个参数看似可有可无,其实是为了用于给_M_call区分传入参数类型是一个智能指针还是一个派生类对象的

  • _M_call实现如下,可以看到,第一个重载的形式是处理派生类对象的,第二个重载的形式是处理智能指针的,代码比较简单,这里就不多作分析了,大家可以自行看一遍就明白了

template
_Res _M_call(_Tp&& __object, const volatile _Class *, _Args&&... __args) const
{
    return (std::forward<_Tp>(__object).*__pmf)(std::forward<_Args>(__args)...);
}

template
_Res _M_call(_Tp&& __ptr, const volatile void *, _Args&&... __args) const
{
    return ((*__ptr).*__pmf)(std::forward<_Args>(__args)...);
}

3.3、在代码中正确使用std::_Mem_fn

示例代码如下,从上面的一大段分析可以知道,我们传入的ages[2]就是之前一直分析的那个用于调用类成员函数的那个传入对象,而ages[3]就是bool Age::compare(const Age& t)所需要的正常的传入参数了,也就是上面的可变参数里面的值。至此std::mem_fn源码也就分析完毕了

#include 
#include 
#include 
#include 

class Age
{
public:
    Age(int v)
        :m_age(v)
    {
    }

    bool compare(const Age& t) const
    {
        return m_age < t.m_age;
    }

    int m_age;
};

bool compare(const Age& t1, const Age& t2)
{
    return t1.compare(t2);
}

int main(int argc, char* argv[])
{
    std::vector ages{1, 7, 19, 27, 39, 16, 13, 18};
    bool ret = std::mem_fn(&Age::compare)(ages[2], ages[3]);
    //std::sort(ages.begin(), ages.end(), std::mem_fn(&Age::compare));

    return 0;
}

以上就是关于“C++11中的std::mem_fn怎么用”这篇文章的内容,相信大家都有了一定的了解,希望小编分享的内容对大家有帮助,若想了解更多相关的知识内容,请关注创新互联行业资讯频道。


网站栏目:C++11中的std::mem_fn怎么用
新闻来源:http://pwwzsj.com/article/jphpog.html