operator< 定義的嚴格弱序**。operator<class MyType {
public:
int a, b, c;
MyType(int a, int b, int c) : a(a), b(b), c(c) {}
// 必須定義嚴格弱序
bool operator<(const MyType& other) const {
// 錯誤示範:只比較 a
// return a < other.a;
// 正確:全鍵比較
if (a != other.a) return a < other.a;
if (b != other.b) return b < other.b;
return c < other.c;
}
};
struct MyTypeCmp {
bool operator()(const MyType& lhs, const MyType& rhs) const {
if (lhs.a != rhs.a) return lhs.a < rhs.a;
if (lhs.b != rhs.b) return lhs.b < rhs.b;
return lhs.c < rhs.c;
}
};
std::set s;
// 或
auto cmp = [](const MyType& l, const MyType& r) {
return std::tie(l.a, l.b, l.c) < std::tie(r.a, r.b, r.c);
};
std::set s(cmp);
class MyType { // 只比較 a
bool operator<(const MyType& o) const { return a < o.a; }
};
std::set s;
s.insert(MyType(1, 2, 3)); // 成功
s.insert(MyType(1, 2, 4)); // 失敗!set 認為與第一個相同
// s.size() == 1
set 判斷兩元素相等的邏輯:
!(a < b) && !(b < a) // 即 equivalent(a, b)
若 operator< 僅比較 a,則 (1,2,3) 和 (1,2,4) 互不小於,被視為**等價**,第二次插入被忽略。
bool operator<(const MyType& o) const {
return std::tie(a, b, c) < std::tie(o.a, o.b, o.c);
// 或手寫:
// if (a != o.a) return a < o.a;
// if (b != o.b) return b < o.b;
// return c < o.c;
}
修正後,(1,2,3) 和 (1,2,4) 可共存,s.size() == 2。
比較函數 comp(a, b) 必須滿足:
comp(a, a) == falsecomp(a, b) == true ⇒ comp(b, a) == falsecomp(a, b) && comp(b, c) ⇒ comp(a, c)equiv(a, b) 且 equiv(b, c) 則 equiv(a, c)違反上述任一條件 → **未定義行為**(可能死循環、崩潰、錯誤結果)。
| 錯誤寫法 | 問題 | 修正 |
|---|---|---|
return a <= o.a; | 違反非自反性 (a==o.a 時為 true) | return a < o.a; |
| 只比較部分成員 | 不同對象被視為等價 | 全鍵字典序比較 |
比較浮點數直接用 < | NaN 導致邏輯崩壞 | 用 std::isnan 處理或避免作 key |
| 修改已在 set 中元素的關鍵成員 | 破壞順序,導致查找失效 | 先 erase,修改,再 insert |
std::tie 自動生成字典序比較:簡潔、不易出錯、支援 C++11 起。std::map 或 std::multimap,而非在 set 中塞入複合結構。std::unordered_set (哈希表) 平均 O(1),需提供 hash 與 ==。set **必須**提供完整、正確的嚴格弱序比較。std::tie 一行搞定字典序,或外部仿函數分離比較邏輯。multiset 或 map。