敢在简历里写“精通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 };
为了兼容更多参数的函数,你可能要特化好多遍这个代码。当没有语法支持时,好多人都这么干。