浏览器标题切换
浏览器标题切换end

关于C++中在模板参数中使用Lambda表达式的问题

问题来源

直接在模板参数中使用lambda表达式不被允许。比如: priority_queue<int, vector<int>, greater<int>> minHeap; 在最小堆定义中,我们第三个模版是 greater<int>,这个模版参数希望我们传入一个类型,而不是函数,因为模版参数在编译时就确定其类型,所以不可以直接使用函数。也就不能和普通的 bool compare(int x, int y) {} 这样子写然后传入(因为这是一个普通函数)。

所以,模版参数我们可以使用 函数对象使用lambda表达式搭配decltype推导函数表达式 的方法,这样子他们的返回值是允许在模版参数中被传入的。(简单说,如果需要实现一个自定义的比较逻辑,应该使用一个函数对象(functor)或 lambda表达式(配合decltype)来作为模板参数。

使用如下:

  1. 返回函数对象:
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;
  1. 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表达式并没有提供一个类型,而是尝试提供一个具体的对象实例,这与模板参数期望的类型信息不匹配。

解决办法:

  1. 使用自定义函数
  2. 使用 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;
}
posted @ 2024-03-21 02:05  抓水母的派大星  阅读(180)  评论(0编辑  收藏  举报