仿函式——Functor

Xiao_Qu發表於2014-05-03

簡單的需求

比如,有一個簡單需求:找到一個vector<string>中,長度小於3的字串的數目。解決方法可能會是:

    int count(const std::vector<std::string>& str_vec, const size_t threshold)
    {
        int size = 0;
        std::vector<std::string>::const_iterator it; 
        for (it = str_vec.begin(); it != str_vec.end(); ++ it) {
            if (it->length() < threshold) {
                ++ size;
            }   
        }   
        return size;
    }

其實,資料STL的同學應該知道有個count_if函式。count_if的功能就是對於某種容器,對符合條件的元素進行計數。count_if包含三個引數,容器的開始地址、容器的結束地址、以及引數為元素型別的函式。

使用count_if的程式碼可以這樣寫:

    bool test(const std::string& str) { return str.length() < 3; }
    int count(const std::vector<std::string>& str_vec)
    {
        return std::count_if(str_vec.begin(), str_vec.end(), test);
    }

但是,這樣有個問題:沒有擴充套件性。比如,判斷的字串由長度3變成5呢?將test函式上面再增加一個長度引數可以嗎?不行,count_if的實現就決定了test必須是單一引數的。既想滿足count_if
的語法要求,又需要讓判斷的函式具有可擴充套件性,這時候就需要functor了。

functor登場

functor的含義是:呼叫它就像呼叫一個普通的函式一樣,不過它的本質是一個類的例項的成員函式(operator()這個函式),所以functor也叫function object
因此以下程式碼的最後兩個語句是等價的:

    class SomeFunctor
    {
    public:
        void operator() (const string& str)
        {
            cout << "Hello " << str << end;
        }
    };

    SomeFunctor functor;
    functor("world");               //Hello world
    functor.operator()("world");    //Hello world

其實,它並不算是STL中的一部分,不過需要STL中的函式都把functor所謂引數之一,functor起到了定製化的作用。functor與其它普通的函式相比,有一個明顯的特點:可以使用成員變數。這樣,就提供了擴充套件性。

繼續上面例子,寫成functor的形式:

    class LessThan
    {
    public:
        LessThan(size_t threshold): _threshold(threshold) {}
        bool operator() (const std::string str) { return str.length() < _threshold; }
    private:
        const size_t _threshold;
    };

    int count(const std::vector<std::string>& str_vec)
    {
        LessThan less_than_three(3);
        return std::count_if(str_vec.begin(), str_vec.end(), less_than_three);
        //LessThan less_than_five(5);
        //std::count_if(str_vec.begin(), str_vec.end(), less_than_five);
    }

    int count_v2(const std::vector<std::string>& str_vec, const size_t threshold)
    {
        return std::count_if(str_vec.begin(), str_vec.end(), LessThan(threshold));
    }

C++11的新玩法

有人可能會說,我已經有了自己實現的判斷函式了,但是直接用又不行,有啥解決辦法嗎?
其實是有的!(我也是最近才發現的)

C++11的標準中,提供了一套函式,能將一個普通的、不符合使用方要求的函式,轉變成一個符合引數列表要求的functor,這實在是太酷了!

比如使用者自己實現的int test(const std::string& str_vec, const size_t threshold)函式,如果能將第二個引數進行繫結,不就符合count_if的要求了嗎?

新標準的C++就提供了這樣一個函式——bind

透過std::bind以及std::placeholders,就可以實現轉化,樣例程式碼如下:

    bool less_than_func(const std::string& str, const size_t threshold)
    {
            return str.length() < threshold;
    }

    //提供 _1 佔位符
    using namespace std::placeholders;                              
    //繫結less_than_func第二個引數為5, 轉化為functor
    auto less_than_functor = std::bind(less_than_func, _1, 5); 
    std::cout << std::count_if(str_vec.begin(), str_vec.end(), less_than_functor) << std::endl;

參考資料

--EOF--

相關文章