function的作用是将具有相同调用形式的不同类型可调用对象进行类型统一。
相同的调用形式可以简单理解为:参数列表和返回值相同。
C++常见可调用对象有:函数、指针、匿名函数(lambda表达式)、函数对象(重载了函数调用运算符的类)以及使用bind创建的对象。
例如以下几个可调用对象具有相同的调用形式:
// 函数
int add(int a, int b) {
return a + b;
}
// lambda表达式
auto sub = [](int a, int b) -> int {
return a - b;
};
// 函数对象
class prod{
public:
int operator() (int a, int b) {
return a * b;
}
};
这些可调用对象的调用形式相同,甚至拥有相同的功能,但是却由于类型千差万别无法统一处理。例如,我们想要统一调用以上可调用对象,使用‘+’来调用add函数,而使用‘-’调用sub匿名函数…
使用分支语句(if else if else)当然是可以做到的,但更推荐以下方式:
/* 使用map映射字符到可调用对象。
但需要注意,映射的前提是function对不同类型的可调用对象
进行了类型统一。 */
map<char, function<int(int, int)>> cacul{
{'+', add},
{'-', sub},
{'*', prod()}
};
cout << cacul['*'](5, 4) << endl;
function基本操作
// T为调用形式resultType(args)
// 默认初始化
function<T> f;
// 显示构造一个空function
function<T> f(nullptr);
// 在f中存储可调用对象的副本
function<T> f(obj);
单独一个function对象可以用来判断是否此其是否保存有可调用对象。已绑定可调用对象则返回true,否则返回false;
function<T> f;
if(f) {
...
} else {
...
}
function对象加上参数列表即可调用其保存的可调用对象。
function<T> f = add;
cout << f(1, 2) << endl; // 返回结果3
需要注意的是,当函数发生重载时不能直接使用函数名来让function保留可调用对象。例如:
int add(int, int);
// person为自定义类型
person add(person a, person b);
// 二义性错误, 即使参数列表不同
function<int(int, int)> f = add;
可以使用存储函数指针来解决问题,因为指针会进行一次匹配:
// 将add(int, int)版本暂存
int(*p)(int, int) = add;
function<int(int, int)> f = p;
也可以使用匿名函数辅助,因为匿名函数中进行了一次正常的函数调用:
function<int(int, int)> f = [](int a, int b) -> int {
return add(a, b);
};
写递归函数时经常会遇到一个问题,递归函数参数中真正随着调用而发生变化的参数很少,一些不变的参数比如:存放数据的数组、常量之类的。虽然在递归过程中不发生变化,但为了获取其数据内容也要出现在递归参数列表,导致递归函数参数列表过于冗长。
要解决这个“麻烦事”,除了将不发生变化的数组之类参数的放到全局,也可以将递归函数放到局部。
以一个简单的逆序打印数组值为例:
// 只有第二个参数必须存在
void dfs(vector<int> v, int i) {
if(i == v.size()) return;
dfs(v, i + 1);
cout << v[i] << endl;
}
将不随调用而变化的参数放到全局
例如将vector放到全局:
vector<int> v;
// 参数列表确实变简洁了,但要维护全局变量
void dfs(int i) {
if(i == v.size()) return;
dfs(v, i + 1);
cout << v[i] << endl;
}
将递归函数放到局部
注意匿名函数是不能直接调用自己的,因为使用 auto 类型说明符声明的变量不能出现在其自身的初始值设定项中
。由于function可以保存可调用对象,可以避免出现上述报错(对于偶尔写写算法题的人来说,真可以说是不错的技巧):
function<void(int)> dfs = [&](int i) {
if(i == v.size()) return;
dfs(i + 1);
cout << v[i] << endl;
};
dfs(0);