关于C++中在模板参数中使用Lambda表达式的问题
问题来源
直接在模板参数中使用lambda表达式不被允许。比如: priority_queue<int, vector<int>, greater<int>> minHeap;
在最小堆定义中,我们第三个模版是 greater<int>
,这个模版参数希望我们传入一个类型,而不是函数,因为模版参数在编译时就确定其类型,所以不可以直接使用函数。也就不能和普通的 bool compare(int x, int y) {}
这样子写然后传入(因为这是一个普通函数)。
所以,模版参数我们可以使用 函数对象 或 使用lambda表达式搭配decltype推导函数表达式 的方法,这样子他们的返回值是允许在模版参数中被传入的。(简单说,如果需要实现一个自定义的比较逻辑,应该使用一个函数对象(functor)或 lambda表达式(配合decltype)来作为模板参数。)
使用如下:
- 返回函数对象:
struct Compare {
bool operator()(const int& a, const int& b) const {
// 自定义比较逻辑
return a > b; // 例如,实现最小堆 (也可以直接用priority_queue<int, vector<int>, greater<int>> minHeap,这里只是展示一下怎么返回函数对象)
}
};
std::priority_queue<int, std::vector<int>, Compare> minHeap;
- lambda表达式(配合decltype):
// 定义lambda表达式作为比较函数
auto comp = [](const int& left, const int& right)
{
return left > right;
};
// 使用 decltype 来推断 lambda 表达式的类型
priority_queue<int, vector<int>, decltype(comp)> minHeap(comp);
原因:
- C++的设计强调 类型安全 和 在编译时确定类型信息,因为这会提高程序的性能和类型安全。
- 直接使用lambda可能会导致难以诊断的错误和理解上的困难,因为lambda表达式的隐式类型不容易在错误消息中明确表示。
- 模板参数的类型:C++中,模板参数需要在编译时就确定其类型。对于类型模板参数(如类模板或函数模板的类型参数),这意味着必须使用具体的类型名称。而lambda表达式在C++中是一个匿名函数对象,每个lambda表达式的类型是唯一且不可重复的,并且这个类型是由编译器在编译时自动生成的。
- Lambda表达式的类型:由于lambda表达式是匿名的,它们没有显式的类型名,所以直接在模板参数中引用lambda表达式的类型是不行的。每个lambda表达式的类型虽然可以通过decltype操作符来推导,但在模板参数中直接写一个lambda表达式并没有提供一个类型,而是尝试提供一个具体的对象实例,这与模板参数期望的类型信息不匹配。
解决办法:
- 使用自定义函数
- 使用 Lambda函数,但是需要和
decltype
结合(推导表达式类型)。
模版中使用自定义函数
当我们需要使用优先队列定义一个小根堆,这样子使用就可以了 priority_queue<int, vector<int>, greater<int>> minHeap;
,里面使用了 vector 存放了int 类型的数据。
但是如果内部元素变成了链表(指针域+数据域),并且我们需要对链表节点进行排序的时候,我们就不能用自带的greater比较器了。 但是我们可以通过自定义函数+函数重载写成如下:
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(nullptr) {}
};
struct compare {
bool operator()(const ListNode* l, const ListNode* r) {
return l->val > r->val;
}
};
class Solution {
public:
ListNode* mergeKLists(std::vector<ListNode*>& lists) {
std::priority_queue<ListNode*, std::vector<ListNode*>, compare> pq;
...
模版中使用Lambda
问题转化为 -> 使用lambda表达式作为priority_queue比较器
如果我们不使用自定义比较函数,而是使用Lambda表达式,那么需要通过一个lambda表达式初始化一个函数对象,并传递这个对象作为 std::priority_queue
的构造参数。然而,直接在模板参数中使用lambda表达式是不允许的,因为lambda表达式的类型是在编译时自动生成的,每个lambda表达式都有其独特的类型。
为了实现这一点,可以使用 std::function
包装比较lambda表达式,但是注意 std::priority_queue
的模板参数需要一个类型,而不是一个对象。因此,一个常见的做法是定义一个函数对象类型,可以通过创建一个结构体或类,其中包含一个调用运算符(())来实现这一点。但是,对于直接在 priority_queue
模板参数中使用lambda表达式,通常采取另一种方法:使用 decltype
关键字推断lambda表达式的类型,并结合使用 auto
关键字来声明比较器对象。
基本使用:
auto compare = [](const ListNode* l, const ListNode* r) { return l->val > r->val; };
priority_queue<ListNode*, vector<ListNode*>, decltype(compare)> miniHeap(compare);
详细使用如下:
#include <iostream>
#include <queue>
#include <vector>
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(nullptr) {}
};
int main() {
auto compare = [](const ListNode* lhs, const ListNode* rhs) {
return lhs->val > rhs->val;
};
// 使用 decltype(comp) 作为 priority_queue 的比较类型
priority_queue<ListNode*, std::vector<ListNode*>, decltype(compare)> minHeap(compare);
....
return 0;
}