C++ STL set的insert函数原理及重载<导致插入差异的原因解析
为什么两个
operator<重载会导致std::set插入结果不同? 这问题问得好!其实核心在于C++ STL里std::set判断元素是否重复的逻辑,以及你写的operator<是否符合它的要求。咱们一步步拆解:
先搞懂std::set的核心特性
std::set是一个基于红黑树的有序容器,它有两个关键规则:
- 容器里的元素是唯一的,不会存在等价的元素
- 元素会按照你定义的排序规则(默认是
std::less,也就是<运算符)自动排序
这里的重点是:set判断两个元素是否"等价"(也就是要不要视为同一个元素不重复插入),不是用==运算符,而是通过以下逻辑:
如果
!(a < b) && !(b < a)为真,就认为a和b等价,不会重复插入
分析第一个operator<的情况
你的第一个重载是:
bool operator<(const Node &p) const { return val > p.val; }
当你插入第二个Node(4)时,和已有的Node(4)比较:
- 现有元素a和新元素b比较:
a.val > b.val→4>4→ false,也就是a < b为false - 反过来
b < a:4>4→ 也是false - 所以
!(a < b) && !(b < a)→!false && !false→ true,set认为这两个元素等价,不会插入第二个4,最终输出就是5 4 3 2 1 0
分析第二个operator<的问题
第二个重载改成了:
bool operator<(const Node &p) const { return val >= p.val; }
同样比较两个Node(4):
a < b→4 >=4→ trueb < a→4 >=4→ 也是true- 这时候
!(a < b) && !(b < a)→!true && !true→ false,set认为这两个元素不等价,所以会把第二个4插入进去,最终输出就变成了5 4 4 3 2 1 0
但这里要敲个警钟:这个重载违反了STL有序容器要求的严格弱序(strict weak ordering)规则!严格弱序要求:
- 自反性:对于任何元素x,
x < x必须为false(你这里x < x返回true,直接违反) - 非对称性:如果
a < b为真,那么b < a必须为假 - 传递性:如果
a < b且b < c,那么a < c必须为真
违反严格弱序会导致容器的行为未定义,这次只是出现了重复元素,实际运行中可能会出现遍历混乱、查找失败甚至程序崩溃的情况,绝对不能这么写!
std::set::insert的具体原理
最后再明确insert函数的工作流程:
- 根据你定义的
<运算符,在红黑树中查找是否存在等价的元素(用!(a < b) && !(b < a)判断) - 如果找到等价元素:
- 不会插入新元素
- 返回一个
pair,其中迭代器指向已存在的元素,bool值为false
- 如果没找到等价元素:
- 把新元素插入到红黑树中合适的位置(保持排序规则)
- 返回一个
pair,其中迭代器指向新插入的元素,bool值为true
内容的提问来源于stack exchange,提问作者sinkinben
相关产品推荐
相关产品推荐

