std :: set有重复的条目

时间:2015-03-18 10:30:00

标签: 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;

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

3 个答案:

答案 0 :(得分: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);
};

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

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)

这样做更安全:

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

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


现在,您的错误如何导致set中的重复键?

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

基本上,所有地狱都破裂了。你可以从中获得崩溃。在您的情况下,症状更加温和(至少目前为止),变形/损坏的数据树导致重复数据的外观

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


答案 1 :(得分: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;

答案 2 :(得分:2)

您确实可以使用标准类std::tuple作为密钥。

然而,可以通过以下方式定义运算符

bool operator <( const Key &rhs ) const
{
    return
    ( a < rhs.a ) ||
    ( !( rhs.a < a ) && ( b < rhs.b ) ) ||
    ( !( rhs.a < a ) && !( rhs.b < b ) && ( c < rhs.c ) );
};

这个操作符可以满足你所需要的是对于a,b和c类的对象,将定义operator <当然对于算术类型它已经定义了。

实际上它与

相同
#include <tuple>

//...

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