問題來源
直接在模板引數中使用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;
}