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 };

为了兼容更多参数的函数,你可能要特化好多遍这个代码。当没有语法支持时,好多人都这么干。

 

posted on 2021-10-27 12:26  __Even  阅读(1453)  评论(0编辑  收藏  举报

导航