本文已参与「新人创作礼」活动,一起开启掘金创作之路。
本文承接上文函数对象提到的的知识点
C++中,我经常使用函数对象,且用std::function来绑定,可是其中的细节,他是如何优雅的进行绑定的,我却一知半解,于是我用我的方式去尝试实现了一个看上去可以用的function
函数指针+模板
既然函数对象也是有类型的,且每一个函数指针都有他所在的类型,比如int add(int,int)的类型是int(int,int),我们可以在一个类里面放上一个函数指针,用来帮助我们
template <typename FUNC> struct function {
using func_type = FUNC *;
func_type oper;
function(func_type fun) : oper(fun) {}
template <typename... ARG>
auto operator()(ARG... arg) -> decltype(oper(arg...)) {
return oper(arg...);
}
};
用到了这么几个特性 auto + decltype 来在编译期决定我们函数的返回类型 可变长模板
以上特性都是基于C++11的,且就算在STL中的实现,很多也是要依赖这些东西的
我们声明了函数类型是 FUNC,然后在编译期我们就可以根据这个来直接得到函数指针
然后根据编译期生成的operator 进行调用
上述例子的缺点:
很显然,我们没有把返回值和传参分开
且这样做,对于仿函数,是不可以进行绑定的
利用模板匹配分离返回值和传参
我们可以利用构造函数的匹配来进行传参和返回值的分离
template <typename Ret, typename... Arg> struct function {
using func_type = Ret (*)(Arg...);
func_type func;
function(Ret (*fun)(Arg...)) : func(fun) {}
Ret operator()(Arg... arg) { return func(arg...); }
};
利用模板特化分离返回值和传参
我们可以根据模板特化,直接指定两者的类型
template <typename Ret, typename... Arg> struct function<Ret(Arg...)> {
using func_type = Ret (*)(Arg...);
func_type func;
function(func_type fun) : func(fun) {}
Ret operator()(Arg... arg) { return func(arg...); }
};
以上两种办法虽然解决了传参分离的问题,可是并没有解决仿函数不可绑定的问题
多态,一种巧妙的手段
回到例子中来观察,对于一类函数对象如
int(int,int)来说,它可能会对于两种形态的东西 1:函数指针 2:仿函数
可不能只简简单单的把这个看成只有两种,这可有成千上万种,对于模板来说,
因为函数类型虽然都是同一个类型,可是仿函数却不一样,因为我们在使用仿函数的时候,
我们不关心其所在的类,但是在模板特化里面,每一个类都会生成一种类型,可是我们的传参和返回值都是固定的
我们需要一点点其他手段来进行类型擦除!
等等,类型擦除?很多类表现出相同的动作?
这不就是多态嘛!
我们可以定义一个基类,把operator()给声明为虚函数,然后我们只需要去调用一个基类指针的仿函数
就可以完美完成我们的需求,也就是不同类都可以统一形式调用
template <typename Ret, typename... Arg> struct function<Ret(Arg...)> {
struct __function {
virtual ~__function() = default;
virtual Ret operator()(Arg... arg) {}
};
template <typename FUNC> struct __function_call : public __function {
~__function_call() {}
virtual Ret operator()(Arg... arg) { return func(arg...); }
FUNC func;
__function_call(FUNC fun) : func(fun) {}
};
}
上述需要特化这个模板的原因是因为,一个仿函数对象,我们已经失去了其函数类型,需要手动指出来
完整代码:
template <typename Ret, typename... Arg> struct function {
using func_type = Ret (*)(Arg...);
func_type func;
function(Ret (*fun)(Arg...)) : func(fun) {}
Ret operator()(Arg... arg) { return func(arg...); }
};
template <typename Ret, typename... Arg> struct function<Ret(Arg...)> {
private:
struct __function {
virtual ~__function() = default;
virtual Ret operator()(Arg... arg) {}
};
template <typename FUNC> struct __function_call : public __function {
~__function_call() {}
virtual Ret operator()(Arg... arg) { return func(arg...); }
FUNC func;
public:
__function_call(FUNC fun) : func(fun) {}
};
__function *call;
public:
template <typename FUNC>
function(FUNC func) : call(new __function_call<FUNC>(func)) {}
~function() { delete call; }
Ret operator()(Arg... arg) { return (*call)(arg...); }
};
最后
写了大半天了,是时候来看看写的东西可不可以满足我们的要求了
int main() {
function fun1 = add;
function<int(int, int)> fun2 = add;
function<int(int, int)> fun3 = add_ob{};
function<int(int, int)> fun4 = [](int a, int b) { return a + b; };
std::cout << fun1(1, 2) << std::endl;
std::cout << fun2(1, 2) << std::endl;
std::cout << fun3(1, 2) << std::endl;
std::cout << fun4(1, 2) << std::endl;
return 0;
}
我们的
function完美的适配了函数类型和对象类型
并且可以和lambda配合使用(你应该知道lambda的本质是一个仿函数吧)