我有一个问题。当我使用带有自定义比较器的std :: set时,其他操作(如erase或count)无法正常工作。 例如:
int sz(int const & n) {
return __builtin_popcount(n);
}
struct comp {
bool operator()(int const & a, int const & b) const {
return sz(a) >= sz(b);
}
};
void solve() {
set<int, comp> s;
for (int i = 0; i < 10; ++i)
s.insert(i);
for (int x : s)
cerr << x << " ";
cerr << "\n";
for (int i = 0; i < 10; ++i)
cerr << s.count(i) << " ";
}
输出将是:
7 9 6 5 3 8 4 2 1 0
0 0 0 0 0 0 0 0 0 0
我如何使用std :: set和自定义比较器,所有操作都能正常工作? 提前谢谢。
答案 0 :(得分:5)
尝试更改
struct comp {
bool operator()(int const & a, int const & b) const {
return sz(a) >= sz(b);
}
};
与
struct comp {
bool operator()(int const & a, int const & b) const {
return sz(a) > sz(b);
} // ---------^
};
(第一个)问题是比较器必须施加严格的弱排序。
因此,comp(a, a) == false
中的每个a
都必须std::set
。
使用比较器,每comp(a, a) == true
个a
。
无论如何:仅当a != b
暗示s(a) != s(b)
时才有效;如果不是这样......好吧......我想你可以试试
struct comp {
bool operator()(int const & a, int const & b) const {
return (sz(a) > sz(b)) || ((sz(a) == sz(b)) && (a > b));
}
};
或类似的东西。
答案 1 :(得分:2)
更多关于理论方面的事情:
根据std::set
的{{1}}比较器(以及标准库中的所有其他&#34;小于比较器&#34;),它需要建立一个{{} 3}}:
a
,comp(a,a) == false
comp(a,b) == true
则comp(b,a) == false
comp(a,b) == true
和comp(b,c) == true
则comp(a,c) == true
为了简短起见,我忽略了无法比较要求的传递性,这是由cppreference文档中的equiv
表达式处理的,但请注意,以上三个都不够。
您可以将比较视为询问&#34;必须a
来b
?&#34;实现假设这是比较要求的问题,并且相等元素的答案是否定的,一个不能出现在另一个之前。您的比较器未通过前两个测试:
comp(0,0)
返回true
comp(1,2)
返回true
,但comp(2,1)
返回false
这不是任意的。为简单起见,想象一个天真的排序数组。您有3 1
并希望插入2
。从头开始,检查comp(2,1)
。它会返回true
,因为这两个位的位数相同,因此您已完成,现在您已2 3 1
。显然,这是不正确的。这并不是说std::set
与排序数组相同,但在确定放置和查找元素的位置时,需要某些继续。严格的弱排序使这个过程保持一致。
对于下降的popcount排序,你真正想要的是一个严格大于比较。因此,这种变化是微不足道的:
return sz(a) > sz(b);
答案 2 :(得分:1)
根据cppreference.com:
二进制谓词,它接受与元素相同类型的两个参数并返回一个bool。表达式comp(a,b),其中comp是这种类型的对象,a和b是键值,如果a被认为是在函数定义的严格弱顺序中的b之前,则返回true。
您的比较器不会这样做。