![265f112c41ed1dee94302abcee742f4a.png](https://i-blog.csdnimg.cn/blog_migrate/39e750d79ceb392bac1b283d05e0f9c0.jpeg)
刷题的时候经常用到堆,即stl里的priority_queue。自定义比较函数有几种方法,各种文章都在互相复读,我就记了lambda表达式那种,也没管实现原理,结果今天做题想在类里定义优先队列和比较函数就遭重了。题外话,某傻篮子复读机网站最近开始弹登陆框,赶紧死了得了☺️。
functor
在c++中functor就是一个定义了operator()运算符的class/struct。然后你就可以创建一个能和普通函数一样用的实例,而且还它可以有成员变量保存状态。
class functor {
public:
a(int val) : x(val) {} // Constructor
int operator()(int y, int z) const { return x + y + z; }
private:
int x;
};
functor func(1);
func(2, 3) // 1 + 2 + 3 = 6
lambda作为一个语法糖,会创建一个nameless functor。原理类似上面,但是还是有区别。lambda只能用auto推导他的类型,因为每一个lambda都是独一无二的,即使参数是相同的类型。或者你可以用std::function将他wrap。还有就是lambda是没有默认构造函数的。
comparator
priority_queue是一个模板类,和sort这种模板函数不同需要指定类型。要自定义compare,根据c++默认参数的规则需要把container的参数也给上。
template <typename T, typename Container = stl::vector<T>, typename Compare = stl::less<typename Container::value_type> >
compare默认参数是less<>这个functor模板类。下面是greater的声明:
template <class T> struct greater {
bool operator() (const T& x, const T& y) const {return x>y;}
typedef T first_argument_type;
typedef T second_argument_type;
typedef bool result_type;
};
在priority_queue中有一个成员变量comp存储Compare的实例,comp默认的值compare(),然而之前已经说过lambda表达式是没有默认构造函数的,所以只能传参给priority_queue的构造函数,同时因为只有编译器才知道lambda的类型,所以得用decltype(cmp)获取类型。
priority_queue(省略): c(), comp(cmp) // copy
priority_queue(省略): c(), comp() //default constructor
priority_queue<int, vector<int>, decltype(cmp)> myqueue(cmp) //lambda
comparator调用的时候comp(a, b)即可,comp是compare的实例。
为什么可以用多种方式自定义函数?
- struct 重载operater<
默认的compare是less,less重载的operator()也要用到struct的operator<,相当于做了一个传递。
2. struct cmp { operator(){}}
这就是定义了一个functor,正是compare需要的。
3. lambda表达式
也是一个functor。
自定义比较函数的priority_queue作为class成员
问题就来了,如果用lambda的方法,需要auto,而auto的成员变量需要static,导致capture的其他类成员变量也要static,这显然是不可接受的。
那换struct cmp的方法,nested class/struct在c++11以前无法访问外面的类的成员,你需要保存一个outer类的引用
struct CompareByArea
{
CompareByArea(Model& aModel):model(aModel)
bool operator() (const Face& f1, const Face& f2) const
{
return model.area(f1) < model.area(f2);
}
Model& model;
};
那用lambda方式怎么写,你需要std::function
function<bool(string&,string&)> cmp = 。。。
priority_queue<string, vector<string>, decltype(cmp)> mq(cmp);
可能新手(指我自己)会按照惯性这样写,然后看到一个error: unknown type name 'cmp'的错误,错误的原因是试图在声明成员变量的时候调用它的构造函数。
正确的做法是用delegating constructor,人话就是把他写在类的初始化列表里。同时注意,cmp和priority_queue的顺序。因为类的初始化顺序和定义顺序相同,和初始化列表顺序无关,需要保证顺序来保证cmp已经被创建了。