仿函数
仿函数又称为函数对象,是一种能够行使函数功能的类,该类重载了operator()运算符,调用仿函数的时候实际上就是通过类对象调用重载后的operator操作符,重载operator()和重载普通的函数效果相同,当参数类型不同时会执行不同的代码逻辑。
仿函数使用
比如自定义了一个仿函数A
#include <typeinfo>
#include <iostream>
using namespace std;
class A{
public:
int operator() (int a, int b){
return a+b;
}
double operator() (double a, double b){
return (a+b)*2;
}
};
int main(){
A a;
auto c1 = a(1, 2);
cout << typeid(c1).name() << " " << c1 << endl;
auto c2 = a(11.3, 2.4);
cout << typeid(c2).name() << " " << c2 << endl;
}
C++标准库中的仿函数存在在于functional.h中,平时用到的最多的是greater,less这两个,这两个函数常被用做排序函数的参数。在排序函数中greater和less这两个函数可以被等效的lambda表达式替换。但是在优先队列中,不能直接使用lambda表达式替换。
所以才会接触到仿函数这个概念。优先队列自定义比较规则,可以使用仿函数或者对被比较的类重载小于操作符。由于C++规定操作符重载必须至少有一个类类型的操作数,因此当优先队列中的元素不是自定义类的时候,则没法对其重载小于操作符,此时只能实用仿函数来实现自定义的比较规则。
以下是为优先队列定义仿函数的示例
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
class comp{
public:
bool operator(const pair<int, int> &a, const pair<int,int> &b) {
return a.second < b.second;
}
};
int main(){
priority_queue<pair<int,int>, vector<pair<int,int>>, comp> q;
q.push({1,3});
q.push({2,4});
q.push({3,6});
auto p = q.top();
cout << p.first << " " << p.second << endl;
}
函数对象 vs 模板类型参数
很多人C++初学者会有这样的疑惑,在sort中可以使用lambda,而在优先队列的定义中不能使用,相反,在优先队列中可以使用仿函数,而在sort中则不能使用仿函数。
这个问题的根源在于没有分清楚仿函数(函数对象)和模板类型参数之间的关系。
sort函数的原型为
template <class RandomAccessIterator, class Compare>
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);
可以看到 comp是一个函数对象,因为lambda对象也属于函数对象,所以可以作为排序的参数。C++中凡是能够调用()运算符的就都是函数对象,包括函数、函数指针、重载了()运算符的对象,以及lambda对象。
优先队列的构造函数原型为
template<
class T,
class Container = std::vector<T>,
class Compare = std::less<typename Container::value_type>
> class priority_queue;
填入尖括号中的应该是类型的名字,即typename,当然不能使用lambda作为typename,但是我们使用decltype来获得lambda的类型,作为typename,然后在构造函数的参数中把lambda示例传进去,就能work。
示例如下
#include<queue>
#include<iostream>
using namespace std;
int main(){
auto comp = [](const pair<int,int> &a, const pair<int,int> &b){
return a.second < b.second;
};
priority_queue<pair<int,int>, vector<pair<int,int>>, decltype(comp)> q(comp);
q.push({1,3}); q.push({2,4}); q.push({3,6});
auto p = q.top();
cout << p.first << " " << p.second << endl;
}