本文动机为在刷力扣时遇到如下错误:
error:reference to non-static member function must be called
1. 使用sort算法自定义排序的几种方法及类内实现出现的问题
sort算法默认使用元素类型的 ‘<’ 运算符,但是当我们希望的排序顺序与 ‘<’ 所定义的顺序不同,或者我们的序列是未定义 ‘<’ 运算符的元素类型,这两种情况下,都需要重载sort的默认行为。
通常,我们通过自定义一个cmp函数(官方称为谓词)作为sort的第三个参数来指定我们指定的排序规则,举例如下:
输入:给定一个二维数组envelopes,其中envelopes[i] = [wi, hi]分别表示信封宽度和高度;
排序规则:按信封宽度升序排列,对于宽度相同的信封,按高度降序排列。
用例:输入:envelopes = [[5,4],[6,4],[6,7],[2,3]],输出:[[2,3],[5,4],[6,7],[6,4]]
(注:这其实是力扣里面的一道hard题的一部分,详情见 LC #354 俄罗斯套娃信封问题)
bool cmp(vector<int>& a, vector<int>& b) {
if(a[0] != b[0]) return a[0] < b[0];
else return a[1] > b[1];
}
void testSort(vector<vector<int>>& envelopes) {
sort(envelopes.begin(), envelopes.end(), cmp);
}
也可以不在外部显式定义cmp函数,而用lambda表达式实现谓词:
void testSort(vector<vector<int>>& envelopes) {
sort(envelopes.begin(), envelopes.end(),
[](const vector<int>& a, const vector<int>& b){
if(a[0] != b[0]) return a[0] < b[0];
else return a[1] > b[1];
});
}
但是,在刷力扣时,函数是实现在类内的,这时候采用方法一在类内定义cmp函数就会报错,代码和报错如下;
class Solution {
public:
bool cmp(vector<int>& a, vector<int>& b) {
if(a[0] != b[0]) return a[0] < b[0];
else return a[1] > b[1];
}
int maxEnvelopes(vector<vector<int>>& envelopes) {
int n = envelopes.size();
// 下面这行排序会报错!
sort(envelopes.begin(), envelopes.end(), cmp);
vector<int> dp(n, 1);
for (int i = 1; i < n; i++) {
for (int j = 0; j < i; j++) {
if (envelopes[j][1] < envelopes[i][1]) {
dp[i] = max(dp[j] + 1, dp[i]);
}
}
}
return *max_element(dp.begin(), dp.end());
}
};
分析:
查看sort源码(文末会贴出)会发现,sort的第三个参数,也就是我们自定义的cmp,只允许接收两个参数,而当在类内定义cmp函数时,如果像上述那样定义,在编译时,编译器会向函数添加隐式参数this,而变成了三个参数,所以编译会报错。
解决办法一:
将类内定义的cmp函数声明为static,此时cmp内不会加入this;
解决办法二:
将cmp函数定义在类外;
解决办法三:
直接用lambda函数。
2. sort部分源码:
sort有两个重载版本:
第一个版本只需传给它两个迭代器就可以了,此时默认升序排序,如下:
第二个版本除了两个迭代器之外,还需要我们提供一个自定义的比较函数(谓词),如下:
此处暂且不讨论sort底层通过怎样的方式进行排序,有兴趣可以自己看下源码就明白了,这里我们关注一下sort对于第三个参数"__comp"是怎样定义的,从下图可以看出,__comp函数只接受两个参数,所以对于类内定义的cmp,因为会引入额外的this参数,导致报错。
(注:从上图可以看出这里用了快排的方法,但sort算法的排序机制远不止如此的,具体实现另行探讨啦)