敢在簡歷里寫“精通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 };
為了兼容更多參數的函數,你可能要特化好多遍這個代碼。當沒有語法支持時,好多人都這么干。