More on the theory side of things:
Per the documented requirements for std::set
's comparator (and every other "less-than comparator" in the standard library), it needs to establish a strict weak ordering:
- For all
a
, comp(a,a) == false
- If
comp(a,b) == true
then comp(b,a) == false
- If
comp(a,b) == true
and comp(b,c) == true
then comp(a,c) == true
To keep it short, I've left out the transitivity of incomparability requirement, which is handled by the equiv
expressions in the cppreference documentation, but note that the above three aren't quite enough.
You can think of the comparison as asking "Must a
come before b
?" The implementation assumes this is the question the comparison is asking and the answer for equal elements is no, one must not appear before the other. Your comparator fails the first two tests:
comp(0,0)
returns true
comp(1,2)
returns true
, but comp(2,1)
returns false
This isn't arbitrary. For simplicity, imagine a naive sorted array. You have 3 1
and want to insert 2
. Starting at the beginning, you check comp(2,1)
. It returns true
because the two have the same number of bits, so you're done and now you have 2 3 1
. Evidently, this is incorrect. That isn't to say std::set
is the same as a sorted array, but it needs something to go on when figuring out where to put and find elements. A strict weak ordering keeps this process consistent.
What you're really after for a descending popcount ordering is a strictly greater-than comparison. Therefore, the change is trivial:
return sz(a) > sz(b);