使用自定义比较器

时间:2017-12-17 22:14:29

标签: c++ c++11 std stdset

我有一个问题。当我使用带有自定义比较器的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和自定义比较器,所有操作都能正常工作? 提前谢谢。

3 个答案:

答案 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) == truea

无论如何:仅当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}}:

  • 适用于所有acomp(a,a) == false
  • 如果comp(a,b) == truecomp(b,a) == false
  • 如果comp(a,b) == truecomp(b,c) == truecomp(a,c) == true

为了简短起见,我忽略了无法比较要求的传递性,这是由cppreference文档中的equiv表达式处理的,但请注意,以上三个都不够。

您可以将比较视为询问&#34;必须ab?&#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。

您的比较器不会这样做。