function是一个类模板,实现了对普通函数、函数对象、lambda表达式、bind表达式、类成员函数等的统一调用。具体用法不不多说,下面举个简答的例子:
#include <iostream>
#include <functional>
using namespace std;
void print(int a,int b,int c) {
std::cout << "a: " << a << " b: " << b << " c: " << c << "\n";
}
int add(int a, int b) {
std:: cout << "a + b: " << a + b << std::endl;
}
int main()
{
// 保存的函数返回值为void,具有3个int型入参
function<void(int, int, int)> fprint = print;
fprint(1, 2, 3);
// 保存的函数返回值为int, 具有两个int型入参
function<int(int, int) > fadd = add;
fadd(100, 200);
return 0;
}
函数的打印结果如下:
a: 1 b: 2 c: 3
a + b: 300
上面函数的用法很简单,但是有两点我特别好奇:
1、模板参数传入方式很特别:
假如我自己定义了一个类模板,用来保存一个具有两个入参的函数指针,函数签名为:
int (int, int),大概会是下面的形式:
// 返回值类型为RT, 两个入参类型分别为T1,T2
template<typename RT, typename T1, typename T2>
class Myfunction {
// 具体实现
};
那么在定义实例化的时候,大概会是这种形式:
Myfunction<int, int, int> f;
但是注意到,如果使用function来保存,function的形式是下面:
function<int(int,int)> f; // 模板参数为int(int,int), 而不是 int, int, int
2、function可以保存具有不同数量入参的函数, 即可以传入不定参数。
比如在给出的例子中,print有3个参数,add具有两个参数。
为了弄清楚上面的两个问题,我看了一下function的源码以及一些资料,主要用到了两个关键技术:
- 模板特化
- 模板不定参数
下面用一个简单的例子来说明,如果能够看懂下面这个例子,相信也就弄明白了上面的两个问题:
#include <iostream>
#include <functional>
using namespace std;
// 定义一个主模板,该模板不需要具体实现,目的为了实现后面模板的特化
template <typename RT, typename... ArgTypes>
class Function;
// 特化Fucntion模板
template <typename RT, typename... ArgsTypes>
// RT为返回值类型,ArgsTypes是可变模板参数包,ArgsTypes...是参数包扩展,
// 扩展之后的参数与模板传入的参数匹配, 这样就可以传入不定参数
class Function<RT(ArgsTypes...)> {
private:
using FuncType = RT(ArgsTypes...);
FuncType* func_;
public:
Function(FuncType func) : func_{func} {}
void operator() (ArgsTypes... args) {
func_(args...);
}
};
void print(int a,int b,int c) {
std::cout << "a: " << a << " b: " << b << " c: " << c << "\n";
}
int main()
{
Function<void(int, int, int)> f = print;
f(1, 2, 3);
return 0;
}