C++ std::function的簡單實現以及函數指針


敢在簡歷里寫“精通C++”或“熟練掌握C++”的人,都已經被面試官問死了……

今天閑來無事,想着怎么實現std::function,反正待着也沒意思。

 

首先通過使用方式下手:

1 myfunction<int(const std::string&)> fc = test_func;
2 int len = fc("asdasd");

  實現完之后,暫時的目標是讓這兩句話能成功跑起來。其中,myfunction 是將要實現類似std::function的類名;test_func 是一個參數類型為 const string&,返回值為int的函數指針;fc為變量名,我要通過它來進行函數調用。

 

顯而易見,myfunction 類是一個模板類,並且模板參數只有一個,那么就可以先這樣做:

1 template<typename T> 
2 class myfunction 
3 {
4 };

接下來怎么辦……

看第二行代碼:

int len = fc("asdasd");

這句話包含大量信息:

  1. 由於 fc 是 myfunction 類的實例化對象,而且它進行了類似 obj(xxx) 的操作,所以,這里需要 operator() 的運算符重載;

  2.根據 1 可知,operator() 重載中,需要類型為 string 參數,也就是 test_func 函數的參數。換句話說,需要知道參數的類型 myfunction 才能泛用;

  3. 同理,需要知道 test_func 函數返回值的類型;

 

綜上,接下來——

 1 template<typename TRet, typename TArg1>
 2 class myfunction<TRet(TArg1)>
 3 {
 4 public:
 5     myfunction() {}
 6     ~myfunction() {}
 7 
 8 public:
 9     TRet operator()(TArg1 arg1)
10     {
11     }
12 
13     TRet(*)(TArg1) operator=(TRet(*)(TArg1) fc) 
14     { 
15     }
16 
17 };

 

其中  class myfunction<TRet(TArg1)>  用到了模板特化。這種寫法並不常見,尤其是里面的模板參數。

 

當然,上述代碼中的函數指針可不能這樣寫,比如形參fc,需要改變一下寫法,這個誰都會

 1 template<typename TRet, typename TArg1>
 2 class myfunction<TRet(TArg1)>
 3 {
 4 public:
 5     myfunction() {}
 6     ~myfunction() {}
 7 
 8 public:
 9     TRet operator()(TArg1 arg1)
10     {
11     }
12 
13     TRet(*)(TArg1) operator=(TRet(*fc)(TArg1) ) 
14     { 
15     }
16 
17 };

 

當我們把真正的可使用指針、兩個運算符重載函數都填上,代碼就已經接近完成了

 1 template<typename TRet, typename TArg1>
 2 class myfunction<TRet(TArg1)>
 3 {
 4 public:
 5     myfunction() : _fc(NULL) {}
 6     ~myfunction() {}
 7 
 8     myfunction(TRet(*fc)(TArg1))
 9         : _fc(fc)
10     {
11     }
12 
13 public:
14     TRet operator()(TArg1 arg1)
15     {
16         if (_fc == NULL)
17         {
18             throw(std::logic_error("The _fc is nullptr!"));
19             return TRet();
20         }
21 
22         return _fc(arg1);
23     }
24 
25     TRet(*)(TArg1) operator=(TRet(*fc)(TArg1))
26     { 
27         _fc = fc; 
28         return _fc; 
29     }
30 
31 private:
32     TRet(*_fc)(TArg1);
33 };

正在我高興之余,我試着編譯了一下……未通過!錯在了第25行 :  TRet(*)(TArg1) operator=(TRet(*fc)(TArg1)) 。也就是說,返回函數指針還不能這樣寫。

於是我轉變了一下寫法,把返回值類型typedef一下:

1 typedef TRet(*TFunc)(TArg1);
2 TFunc operator=(TRet(*fc)(TArg1))
3 { 
4     _fc = fc; 
5     return _fc; 
6 }

這樣就可以了。

但是我從小就頭鐵,我就想知道,如果不用typedef該怎么辦?

試了好久都沒成功,直到我看見了這個:

TRet(*fc)(TArg1)

也就是說,函數指針的定義法就不能按照普通的 type name; 的形式,應當是:

type_ret(*name)(type_arg)

這樣的。所以,我把operator=函數改成了這樣:

TRet(*operator=(TRet(*fc)(TArg1)))(TArg1) 

實際上,就是把  operator=(TRet(*fc)(TArg1))  這個沒有返回值的函數體扔在了前面的星號后。

不就是遞歸嗎?我也會。

至此,整個類也就完成了,如下:

#include <iostream>
#include <string>

int test_func(const std::string& a) { return a.size(); }

template<typename T> 
class myfunction 
{
};

template<typename TRet, typename TArg1>
class myfunction<TRet(TArg1)>
{
public:
    myfunction() : _fc(NULL) {}
    ~myfunction() {}

    myfunction(TRet(*fc)(TArg1))
        : _fc(fc)
    {
    }

public:
    TRet operator()(TArg1 arg1)
    {
        if (_fc == NULL)
        {
            throw(std::logic_error("The _fc is nullptr!"));
            return TRet();
        }

        return _fc(arg1);
    }

    TRet(*operator=(TRet(*fc)(TArg1)))(TArg1) 
    { 
        _fc = fc; 
        return _fc; 
    }

private:
    TRet(*_fc)(TArg1);
};

int main()
{
    int (*fc)(const std::string&) = test_func;
    size_t len = fc("asd");

    myfunction<int(const std::string&)> fc2 = test_func;
    int len2 = fc2("asdasd");

    std::cout << len2 << std::endl;

    return 0;
}

 

因為不使用c++11的語法,也就是不能使用可變參數模板,此類只能用於一個參數的函數。如果兩個的該怎么辦?

那就再特化一遍:

 1 template<typename TRet, typename TArg1, typename TArg2>
 2 class myfunction<TRet(TArg1, TArg2)>
 3 {
 4 public:
 5     myfunction() : _fc(NULL) {}
 6     ~myfunction() {}
 7 
 8     myfunction(TRet(*fc)(TArg1, TArg2))
 9         : _fc(fc)
10     {
11     }
12 
13 public:
14     TRet operator()(TArg1 arg1, TArg2 arg2)
15     {
16         if (_fc == NULL)
17         {
18             throw(std::logic_error("The _fc is nullptr!"));
19             return TRet();
20         }
21 
22         return _fc(arg1, arg2);
23     }
24 
25     TRet(*operator=(TRet(*fc)(TArg1, TArg2)))(TArg1, TArg2) 
26     { 
27         _fc = fc; 
28         return _fc; 
29     }
30 
31 private:
32     TRet(*_fc)(TArg1, TArg2);
33 };

為了兼容更多參數的函數,你可能要特化好多遍這個代碼。當沒有語法支持時,好多人都這么干。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM