本文记录一下 function 函数对象的实现过程。

function 对象实际上就是对函数指针的封装,比如有如下一个函数

void hello(std::string& str)
{
    std::cout << str << std::endl;
}

在主函数中这样使用 function 对象。

int main()
{
    function<void(string)> func = hello;
    return 0;
}

针对这种使用方法,写出对应的类模板。

// 声明一个类模板
template<typename Fty>
class myfunction{};

// 写出特化版本
template<typename R, typename A1>
class myfunction<R(A1)>
{
public:
    // typedef R(* PFUNC)(A1);  声明函数指针类型 或者使用 using 关键字
    using PFUNC = R(*)(A1);
    // 构造函数中接收函数指针
    myfunction(PFUNC pfunc): _pfunc(pfunc){}

    // 重载小括号
    // 返回值类型是 R, 形参类型是 A1
    R operator(A1 arg)
    {
        return _pfunc(arg);
    }
private:
    PFUNC _pfunc;
}

如果传入的函数是下面这样的

int add(int a,int b)
{
    return a + b;
}

写出对应的特化版本也很简单。

template<typename R,typename A1,typename A2>
class myfunction<R,A1,A2>
{
public:
    using PFUNC = R(*)(A1,A2);
    myfunction(PFUNC pfunc): _pfunc(pfunc){}
    R operator()(A1 arg1, A2 arg2)
    {
        return _pfunc(arg1,arg2);
    }
private:
    PFUNC _pfunc;
}

写到这里,想必大家都想到了一个问题,就是函数指针的类型是无穷的,没办法写出无穷个特例化版本。
对这个问题进行简单的抽象,一个函数指针其实就是包括 返回值类型 和 参数列表。上面的两个特例化版本中唯一的不同也就是形参列表的不同。 C++11 的可变模板参数提供了解决方案。

// 返回值类型 R, 可变参数类型 A
// 注意 ... 的位置
template<typename R,typename... A >
class myfunction<R(A...)>
{
public:
    // 声明函数指针类型
    using PFUNC = R(*)(A...);
    myfunction(PFUNC pfunc): _pfunc(pfunc){}
    // 重载小括号
    // 返回值类型 R,参数列表类型 A...
    // 也是注意 ... 的位置
    R operator()(A... arg)
    {
        return _pfunc(arg...);
    }
private:
    PFUNC _pfunc;
}