std :: set有重复的条目

Sin*_*ndi 14 c++ stl

我有一组3个整数的元组,我不想要任何重复.也就是说,我不希望2个条目具有相同的3个值.

这是我的代码.

struct Key{
    unsigned a;
    unsigned b;
    unsigned c;
  public:
    Key(unsigned _a, unsigned _b, unsigned _c) :
        a(_a),
        b(_b),
        c(_c) {}
    bool operator<(const Key& rhs) const
    {
        if (a < rhs.a) {
            return true;
        }
        if (b < rhs.b) {
            return true;
        }
        if (c < rhs.c) {
            return true;
        }
        return false;
    };
};
std::set<Key> myset;
Run Code Online (Sandbox Code Playgroud)

但我myset有时会看到重复.我无法准确捕捉到什么序列导致添加重复条目.它并不总是发生.我的问题是,我的operator<功能有什么本质上的错误吗?

Lig*_*ica 35

这几乎是正确的!但是你太快了.

bool operator<(const Key& rhs) const
{
    if (a < rhs.a)
        return true;
    if (a > rhs.a)
        return false;

    if (b < rhs.b)
        return true;
    if (b > rhs.b)
        return false;

    return (c < rhs.c);
};
Run Code Online (Sandbox Code Playgroud)

否则,例如,以下结果会给出错误的结果:

Key lhs{3,1,0};
Key rhs{2,2,0};

assert(lhs < rhs); // passes, wrongly, because !(3 < 2) but then (1 < 2).
                   // you need an immediate `return false` when !(3 < 2)
Run Code Online (Sandbox Code Playgroud)

做这样的事情比较安全:

bool operator<(const Key& rhs) const
{
    return std::tie(a, b, c) < std::tie(rhs.a, rhs.b, rhs.c);
}
Run Code Online (Sandbox Code Playgroud)

C++的标准库已经知道如何处理它,所以你不必这样做.


现在,你的bug如何导致重复密钥set

Set的内部算法依赖于排序是严格的弱排序 - 当你打破这个前提条件时,你打破了管理内部树的算法,内部树是使用这种排序作为其圣经构造和排列的.

基本上,所有的地狱都破裂了.你可能会因此而崩溃.在您的情况下,症状稍微更温和(至少现在),变形/损坏的数据树导致重复数据的出现.

如果您通过打破先决条件并导致UB开始,尝试推断导致特定结果的特定事件链是愚蠢的.


  • @SindhuraBandi:我认为你已经明白了,因为你已经做了逻辑演绎以检查比较器.我已经在答案中添加了原因. (2认同)

小智 9

operator<()是不相符的,因为key1<key2key2<key1可以都true(例如:key1={1,3,0},key2={3,1,0}).您应该为成员变量赋予优先权:

    if (a < rhs.a) {
        return true;
    } else if (a == rhs.a) {
        if (b < rhs.b) {
            return true;
        } else if (b == rhs.b) {
            if (c < rhs.c) {
                return true;
            }
        }
    }
    return false;
Run Code Online (Sandbox Code Playgroud)