文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

浅谈C++11的std::mem_fn源码解析

2024-04-02 19:55

关注

1、源码准备

本文是基于gcc-4.9.0的源代码进行分析,std::mem_fn是C++11才加入标准的,所以低版本的gcc源码是没有std::mem_fn的,建议选择4.9.0或更新的版本去学习,不同版本的gcc源码差异应该不小,但是原理和设计思想的一样的,下面给出源码下载地址
http://ftp.gnu.org/gnu/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<Age> 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<typename _Tp, typename _Class>
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<typename _Res, typename _Class, typename... _ArgTypes>
class _Mem_fn<_Res (_Class::*)(_ArgTypes...)> : public _Maybe_unary_or_binary_function<_Res, _Class*, _ArgTypes...>
{
    typedef _Res (_Class::*_Functor)(_ArgTypes...);

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

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

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

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

    template<typename _Tp, typename... _Args>
    using _RequireValidArgs3 = _Require<is_base_of<_Class, _Tp>, _AllConvertible<_Pack<_Args...>, _Pack<_ArgTypes...>>>;

public:
    typedef _Res result_type;

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

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

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

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

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

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

private:
    _Functor __pmf;
};

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


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

template<typename _Tp, typename... _Args>
_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 <functional>
#include <iostream>
#include <algorithm>
#include <vector>

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<Age> 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;
}

4、总结

std::mem_fn在函数式编程中的作用是非常大的,我们可以使用std::mem_fn生成指向类成员函数的指针的包装对象,该对象可以存储,复制和调用指向类成员函数的指针。而我们实际使用的是std::mem_fn的返回值std::_Mem_fn这个类,而我们在调用std::_Mem_fn中重载的()方法时,可以使用类对象、派生类对象、对象引用(包括std::reference_wrapper)、对象的右值引用、指向对象的指针(包括智能指针)来作为第一个参数传递进去。

到此这篇关于浅谈C++11的std::mem_fn源码解析的文章就介绍到这了,更多相关C++11 std::mem_fn源码内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯