std::function的简易实现

本节我们来实现一个简易的std::function

我们知道std::function是用来包装可调用对象的,在C++中,可调用对象包括 普通函数lambda表达式重载了()操作符的类对象类静态函数类成员函数这几类。

C++程序的编译顺序:预处理(xxx.i) 编译(xxx.s) 汇编(xxx.obj) 链接(xxx.exe / linux可执行文件)
模板类(函数)的实例化,是在 预处理之后,编译之前;所以模板并不是真正可执行的C++代码,应该将它理解成一个模具,我们使用什么样的材料(模板参数),就会生成什么样的代码;

1.模板函数的模板参数是可以使用编译器自动推导,而模板类的模板参数,却需要显示指定,无法使用自动推导(C++17之后可推导)
使用模板函数自动推导模板参数的前提是: 模板函数中的函数参数,必须要和模板参数有一定的联系,才能利用函数参数的类型,推导出模板参数的类型

2.模板类(函数)的模板参数,其实只是一个类型占位符;我们可以赋予这个占位符任何意义,模板类(函数)的实现也是基于这个我们赋予的意义来进行编写的;所以在使用模板类(函数)时,只要传入的模板参数能满足我们赋予的意义,这个模板类(函数)就能正常运行**


首先回顾一下 std::function的使用,举个比较简单的例子:

	int func(int x, int y) {
    return x + y; }
	std::function<int, (int, int)> f1(func);		//使用1
	std::function<int, (int, int)> f2([int x, int y](){
    return x + y; });	//使用2

	class Test
	{
   
		int memFunc(int x, int y) {
    return x + y; }
	}
	Test t;
	std::function<int, (int, int)> f2(&Test::memFunc, &t);		//使用3

可以看到,我们给std::function传递模板参数时,模板参数的形式为 ReturnType (Params...)
观察使用1 使用2可以发现,构造函数的参数为一个可以被执行的函数(1中为普通函数,2中为lambda表达式),所以我们这里可以将构造函数写成一个模板函数,我们传递一个可执行的函数进来,无论是普通函数还是lambda表达式,模板参数类型交给编译器去推导就行了

结合我们以上发现的特点,最容易想到形式是:
模板参数类型为 返回值类型 参数包, 构造函数为模板函数,构造函数参数为一个可执行的函数; 然后重载 ()操作符,将参数包转发到构造函数传递的可调用函数中去,执行一下该函数即可;

//代码段1
template <typename Ret, typename... Args>
struct myfunction<Ret(Args...)>
{
   
    template <typename Functor>
    myfunction(Functor&& func)
    {
   
    	// 将 func 存储到 m_func 中, 
    	// m_func(std::forward<Functor>(func));
    }

    Ret operator()(Args&&... args)
    {
   
    	//调用 m_func 即可
//        return m_func(std::forward<Args>(args)...);
    }
};

这里其实存在一个问题,那就是我们其实并没有这个m_func成员变量;这个成员变量的类型应该是和构造函数的模板参数一个类型(Functor类型),但是这个Functor只对构造函数可见,所以我们没办法定义一个 Functor m_func;的成员变量;

那怎么解决?

  • 观察我们上面的伪代码可以发现,将 Functor模板参数从构造函数的模板参数,移动到类模板参数中, 这样就扩大了模板参数的可见范围,则可以在类中定义Functor类型的成员变量;那么自然构造函数就不再是模板函数
//代码段2
template <typename Functor, typename Ret, typename... Args>
struct myfunction
{
   
   myfunction(Functor&& func)
   {
   
       m_func(std::forward<Functor>(func));
   }

   Ret operator()(Args&&... args)
   {
   
       return m_func(std::forward<Args>(args)...);
   }
   Functor m_func;
};

这样做固然可以,但是使用时我们是不是得传递这个模板类的模板参数,还是使用以上的例子,我们现在必须得这样使用这个模板类 myfunction<decltype(func), int (int,int)> f;, 其中第一个模板参数Functor, 需要我们自己显示指定;这显然会加大使用负担,且与std::function<int (int, int)>的形式不符;

结合以上的思考,将第一个模板参数Functor放到构造函数中去,由编译器进行推导,而不是定义myfunction时显示指定类型,这是比较合理的,也更符合使用习惯;所以我们还是要在代码段1的基础上进行改进;

那么现在问题变成了: 怎样才能让代码段1支持存储Functor类型的成员变量呢?

有了以上的思考,我们知道,将模板参数的作用域从函数提升到中,就可以扩大可见性
我们可以构造一个辅助类,将构造函数的模板参数变成辅助类的类模板参数,这样就可以将辅助类的类模板参数存储在辅助类中,然后想办法在myfunction中存储辅助类的指针或对象,在myfunction重载的()中调用存储在辅助类中的可调用函数,思考后代码如下:

template <typename Ret, typename... Args>
struct myfunction
{
   
    template <typename Functor>
    myfunction(Functor&& func)		//希望此处使用编译器自动推导,将Functor类型存储在 CallAble模板类中
    {
   
        m_pCallAble = new CallAble<Functor>(std::forward<Functor>(func));
    }

    Ret operator()(Args&&... args)
    {
   
        if (m_pCallAble != nullptr
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值