rlbond 的比较器不会阻止插入比较相等的元素。显然,考虑到字符限制,很难在 cmets 中证明这一点,因为 rlbond 似乎认为 std::set 保证它永远不会包含两个带有 !compare(a,b) && !compare(b,a) 的元素作为他的比较器。但是,rlbond 的比较器没有定义严格的顺序,因此不是 std::set 的有效参数。
#include <set>
#include <iostream>
#include <iterator>
#include <algorithm>
struct BrokenOrder {
int order;
int equality;
public:
BrokenOrder(int o, int e) : order(o), equality(e) {}
bool operator<(const BrokenOrder &rhs) const {
return order < rhs.order;
}
bool operator==(const BrokenOrder &rhs) const {
return equality == rhs.equality;
}
};
std::ostream &operator<<(std::ostream &stream, const BrokenOrder &b) {
return stream << b.equality;
}
// rlbond's magic comparator
struct LessThan : public std::binary_function<BrokenOrder, BrokenOrder, bool> {
bool operator()(const BrokenOrder& lhs, const BrokenOrder& rhs) const
{
return !(lhs == rhs) && (lhs < rhs);
}
};
int main() {
std::set<BrokenOrder,LessThan> s;
for (int i = 0; i < 5; ++i) {
s.insert(BrokenOrder(i,i));
}
for (int i = 0; i < 5; ++i) {
s.insert(BrokenOrder(10-i,i));
}
std::copy(s.begin(), s.end(),
std::ostream_iterator<BrokenOrder>(std::cout, "\n"));
}
输出:
0
1
2
3
4
3
2
1
0
重复。魔法比较器失败了。集合中的不同元素具有相同的equality 值,因此与operator== 比较相同,因为在插入过程中,集合从未将新元素与其副本进行比较。唯一被排除的重复项是 4,因为这两个 4 的排序顺序为 4 和 6。这使它们在集合中足够接近,可以相互比较。
来自 C++ 标准:25.3:3“为了使算法正常工作,comp 必须对值进行严格的弱排序”。
25.3:4 "...要求 comp 和 equiv 都是传递关系:
comp(a,b) && comp(b,c) implies comp(a,c)"
现在,考虑元素a = BrokenOrder(1,1)、b = BrokenOrder(2,2)、c = BrokenOrder(9,1) 和comp 当然等于魔法比较器。那么:
-
comp(a,b) 为真,因为 1 != 2(相等)和 1
-
comp(b,c) 为真,因为 2 != 1(相等)和 2
-
comp(a,c) 为假,因为 1 == 1(相等)