std :: map不会删除自定义对象的重复项

时间:2014-05-11 18:45:41

标签: c++ map comparator standard-library

所有

所以,我创建了一个Comparable基类(一个Java),它完成了在C ++中重载比较运算符的工作。这是:

template <class T>
class Comparable {
public:
  bool operator== (const T& rhs) const { return this->compare(rhs) == 0; }
  bool operator!= (const T& rhs) const { return !(*this == rhs); }
  bool operator<  (const T& rhs) const { return this->compare(rhs) < 0; }
  bool operator<= (const T& rhs) const { return (*this == rhs) || (*this < rhs); }
  bool operator>  (const T& rhs) const { return this->compare(rhs) > 0; }
  bool operator>= (const T& rhs) const { return (*this == rhs) || (*this > rhs); }
protected:
  virtual int compare (const T& rhs) const = 0;
};

子类对象既有ID,也有需要作为排序键的数据。我已经实现了每个,以便具有相同ID的对象返回0,如果它们不是相同的ID,则根据它们的数据进行排序。这是一个例子:

int Foo::compare(const Foo& rhs) const
{
    if (_id == rhs._id)
        return 0;
    // _value is integer data; comparison should sort on this
    if (_value == rhs._value)
    {
        // OK, same data, now sort on ID's
        return _id.compare(rhs._id);
    }
    // Sort on value
    return _value - rhs._value;
}

到目前为止,我觉得很好。

但是,当我尝试将Foo对象存储在std :: set容器中时,该集合不会消除重复。也就是说,它中仍然会有包含相同ID的对象,即使它应该被认为是相同的。

有谁知道发生了什么事?

编辑:关于代码设计的原因有很多问题。

我需要满足两个条件:

  1. 具有相同ID的对象必须被视为“相同”对象,无论其值如何。
  2. 不具有相同ID的对象必须根据其值进行排序(该值取决于对象的类型)。
  3. 这是因为创建这些对象的数据来自未经验证的来源。如果该源为具有相同ID但具有不同值的数据提供数据,则该数据无效。

    编辑2:关于std :: map的严格弱排序。假设你有两个Foo对象,A和B.

    如果他们的ID相等,那么A&lt; B是假的,B&lt; A是假的。 如果他们的ID不相等,那么A&lt; B取决于它们的值是真还是假,并且B

    这不符合严格的订购规则吗?

    在任何情况下,如果std :: set使用小于运算符(默认情况下),它是否应该按设计工作?

    编辑3:std::set,而非std::map。那真是我的愚蠢。道歉。

2 个答案:

答案 0 :(得分:4)

std::map无法通过operator==确定重复项。它使用operator< 1 ,因为它无论如何都需要使用它来确定顺序。您的operator<已损坏。它需要强制执行strict weak ordering

比较失败的方式是以下属性(称为传递性)。对于3个对象,如果A < BB < C,则必须是A < C

因此,请考虑三个对象ABCA.id != B.idA.value < B.valueA < BC.id == B.id。现在,C.id != A.id(因此C.value < A.value),但C < A。所以A < BC。因此,< B应为C.id == B.id。但事实并非如此,因为compare

这样做的一种方法是定义int Foo::compare(const Foo& rhs) const { if (_id < rhs._id) return -1; if (rhs._id < _id) return 1; return _value - rhs._value; } 函数,如下所示:

std::map

如果您无法使用它,并且找不到其他方法来强制执行正确的排序,那么您根本无法将对象用作{{1}}中的键。

<子> 1。如果rhs不小于lhs,并且lhs不小于rhs,那么暗示它们是相等的。您还可以提供替代仿函数,只要它还强制执行严格的弱序排序。

答案 1 :(得分:3)

如果您没有严格的弱订单,则不能使用map,如果您这样做,则不能抱怨,但它不起作用。这是map的前提条件。

编辑:

您的compare未实现您指定的语义 - 如果您在第二次测试中_value == rhs._value,则应返回0。

但是通过这个修复,你仍然没有一致的顺序 - 请参阅Benjamin Lindley在评论中给出的例子。基本上你的排序没有意义 - 你需要修复你的语义,或者停止使用需要一致排序的标准库组件。