溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

C++11中std::mem_fn的用法

發(fā)布時間:2021-06-18 13:39:23 來源:億速云 閱讀:242 作者:chen 欄目:開發(fā)技術(shù)

本篇內(nèi)容介紹了“C++11中std::mem_fn的用法”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!

目錄
  • 1、源碼準備

  • 2、通過一個簡單的例子來了解std::mem_fn的作用

  • 3、std::mem_fn源碼解析

    • 3.1、std::mem_fn解析

    • 3.2、std::_Mem_fn解析

    • 3.3、在代碼中正確使用std::_Mem_fn

  • 4、總結(jié)

    1、源碼準備

    本文是基于gcc-4.9.0的源代碼進行分析,std::mem_fn是C++11才加入標準的,所以低版本的gcc源碼是沒有std::mem_fn的,建議選擇4.9.0或更新的版本去學習,不同版本的gcc源碼差異應(yīng)該不小,但是原理和設(shè)計思想的一樣的,下面給出源碼下載地址
    http://ftp.gnu.org/gnu/gcc

    2、通過一個簡單的例子來了解std::mem_fn的作用

    算法是C++標準庫中非常重要的組成部分,C++通過算法+容器的方式將數(shù)據(jù)結(jié)構(gòu)和算法進行了分離,這樣可以使用戶編寫代碼的時候獲得最大限度的靈活性。假設(shè)我們有如下類:

    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是額外定義的一個比較函數(shù),通過這個函數(shù)來選擇比較的對象并決定比較的結(jié)果

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

    嚴格來講,算法中要求的并不是函數(shù),而是一個可調(diào)用對象。C++中的可調(diào)用對象包括函數(shù)、函數(shù)對象、Lambda表達式、參數(shù)綁定等等,它們都可以作為算法的傳入?yún)?shù),但是如果我們按如下來傳入?yún)?shù)的話,則會在編譯過程中出現(xiàn)錯誤

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

    因為&Age::compare是類成員函數(shù),并非一個可調(diào)用對象,如果我們要將它作為比較的參數(shù)傳遞進去的話,就得用std::mem_fn修飾它,如下所示

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

    從上面的例子可以看到,std::mem_fn的作用就是將類的成員函數(shù)轉(zhuǎn)換為一個可調(diào)用對象,那么問題來了,std::mem_fn是如何實現(xiàn)這種功能的呢?下面讓我們通過分析源碼,來揭開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是一個模板函數(shù),傳入?yún)?shù)為指向_Class類里面的某個成員函數(shù)的指針,其返回值為_Tp,而該模板函數(shù)返回的值為_Mem_fn<_Tp _Class::*>,接下來看一下_Mem_fn的實現(xiàn)

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

    從源代碼中可以看出以下幾點信息:

    • 該類繼承于_Maybe_unary_or_binary_function,由于_Maybe_unary_or_binary_function和本文分析的內(nèi)容沒有太大關(guān)聯(lián),大家可以自行百度查詢其用法,這里就不多作介紹了

    • 類中有一個成員__pmf,其類型是指向上一節(jié)傳入mem_fn的那個類成員函數(shù)的指針,由構(gòu)造函數(shù)初始化

    • 接下來重點看一下類中六個重載的()運算符函數(shù),里面的操作大同小異,基本都是通過__pmf對應(yīng)的類的對象(多種形式)來調(diào)用__pmf成員函數(shù)的:

      • 第一個函數(shù)_Res operator()(_Class& __object, _Args&&… __args):可以看到,其比原始的類成員函數(shù)多要求了一個傳入?yún)?shù),也就是__object,類型是一個類對象的引用,從函數(shù)的實現(xiàn)中可以看到原理就是通過這個類對象來直接調(diào)用先前那個類成員函數(shù)的(沒有這個類對象就調(diào)用不成立了,因為類成員函數(shù)是無法直接調(diào)用的,這也是std::mem_fn存在的意義)

      • 第二個函數(shù)_Res operator()(_Class&& __object, _Args&&… __args):可以看到該方法第一個傳入?yún)?shù)是一個右值引用對象,里面的實現(xiàn)就是通過std::move將對象進行轉(zhuǎn)移而已,其它處理與前面是完全一樣的

      • 第三個函數(shù)_Res operator()(_Class* __object, _Args&&… __args):可以看到該方法傳入了一個對象指針,其它處理與前面是完全一樣的

      • 第五個函數(shù)_Res operator()(reference_wrapper<_Tp> __ref, _Args&&… __args):可以看到該方法傳入了一個被std::reference_wrapper包裝的引用,流程和前面的基本一致,比較簡單,這里就不多作分析了(關(guān)于std::reference_wrapper的問題大家可以看一下這篇文章《C++11的std::ref、std::cref源碼解析》,里面有通過源碼分析對std::reference_wrapper作出了詳細的介紹,這里就不重復說明了)

      • 第四個函數(shù)_Res operator()(_Tp&& __object, _Args&&… __args):這個就比較復雜了,這個函數(shù)是為了處理傳入?yún)?shù)是智能指針或者派生類對象的一個情況的??梢钥吹胶瘮?shù)里調(diào)用了_M_call方法,第二個參數(shù)看似可有可無,其實是為了用于給_M_call區(qū)分傳入?yún)?shù)類型是一個智能指針還是一個派生類對象的

    • _M_call實現(xiàn)如下,可以看到,第一個重載的形式是處理派生類對象的,第二個重載的形式是處理智能指針的,代碼比較簡單,這里就不多作分析了,大家可以自行看一遍就明白了

    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]就是之前一直分析的那個用于調(diào)用類成員函數(shù)的那個傳入對象,而ages[3]就是bool Age::compare(const Age& t)所需要的正常的傳入?yún)?shù)了,也就是上面的可變參數(shù)里面的值。至此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、總結(jié)

    std::mem_fn在函數(shù)式編程中的作用是非常大的,我們可以使用std::mem_fn生成指向類成員函數(shù)的指針的包裝對象,該對象可以存儲,復制和調(diào)用指向類成員函數(shù)的指針。而我們實際使用的是std::mem_fn的返回值std::_Mem_fn這個類,而我們在調(diào)用std::_Mem_fn中重載的()方法時,可以使用類對象、派生類對象、對象引用(包括std::reference_wrapper)、對象的右值引用、指向?qū)ο蟮闹羔槪òㄖ悄苤羔槪﹣碜鳛榈谝粋€參數(shù)傳遞進去。

    “C++11中std::mem_fn的用法”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

    向AI問一下細節(jié)

    免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI