刷题的时候经常用到堆,即stl里的priority_queue。自定义比较函数有几种方法,各种文章都在互相复读,我就记了lambda表达式那种,也没管实现原理,结果今天做题想在类里定义优先队列和比较函数就遭重了。题外话,某傻篮子复读机网站最近开始弹登陆框,赶紧死了得了☺️。
functor
在c++中functor就是一个定义了operator()运算符的class/struct。然后你就可以创建一个能和普通函数一样用的实例,而且还它可以有成员变量保存状态。
class
lambda作为一个语法糖,会创建一个nameless functor。原理类似上面,但是还是有区别。lambda只能用auto推导他的类型,因为每一个lambda都是独一无二的,即使参数是相同的类型。或者你可以用std::function将他wrap。还有就是lambda是没有默认构造函数的。
comparator
priority_queue是一个模板类,和sort这种模板函数不同需要指定类型。要自定义compare,根据c++默认参数的规则需要把container的参数也给上。
template
compare默认参数是less<>这个functor模板类。下面是greater的声明:
template
在priority_queue中有一个成员变量comp存储Compare的实例,comp默认的值compare(),然而之前已经说过lambda表达式是没有默认构造函数的,所以只能传参给priority_queue的构造函数,同时因为只有编译器才知道lambda的类型,所以得用decltype(cmp)获取类型。
priority_queue
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
那用lambda方式怎么写,你需要std::function
function
可能新手(指我自己)会按照惯性这样写,然后看到一个error: unknown type name 'cmp'的错误,错误的原因是试图在声明成员变量的时候调用它的构造函数。
正确的做法是用delegating constructor,人话就是把他写在类的初始化列表里。同时注意,cmp和priority_queue的顺序。因为类的初始化顺序和定义顺序相同,和初始化列表顺序无关,需要保证顺序来保证cmp已经被创建了。