【问题标题】:Does std::multiset guarantee insertion order?std::multiset 是否保证插入顺序?
【发布时间】:2011-02-08 06:22:00
【问题描述】:

我有一个std::multiset,它存储class A 的元素。我已经为这个类提供了我自己的operator< 实现。我的问题是,如果我在这个多重集中插入​​两个等效对象,它们的顺序是否得到保证?例如,首先我将对象a1 插入到集合中,然后将等效对象a2 插入到该集合中。当我遍历集合时,我可以期望 a1 出现在 a2 之前吗?如果没有,有没有办法使用 multiset 来实现这一点?

【问题讨论】:

    标签: c++ stl data-structures standard-library


    【解决方案1】:

    在 C++03 中,不能保证 inserterase 保留相对顺序。然而,这在 C++0x 中有所改变:

    n3092, §23.2.4/4:如果关联容器对于每个键最多包含一个元素,则它支持唯一键。否则,它支持等效键。 set 和 map 类支持唯一键; multiset 和 multimap 类支持等效键。 对于多重集和多重映射,插入和擦除保留等效元素的相对顺序。 强调我的。

    这在defect report 中进行了讨论。 This page 是关于这个问题的 cmets 的集合,它写得很好,也很充实。 (我非常推荐在之前的“概述”链接上阅读这篇文章。)

    在该评论页面中,您可以找到当前实现的比较,因此您可以检查您打算使用的实现是否符合您的预期。

    我想不出一种方法来强行将您想要的命令从我的脑海中排除。 :/

    【讨论】:

    • 也不使用多重集。 std::map<A, std::vector<A> > 是我目前最好的选择...
    • @Matt:是的,你必须手动完成多重设置的行为太糟糕了:/
    • N1780 和 DR 371 明确表示所有当前的实现都提供稳定性,这是 RB 树的自然属性。但问题不在于现有元素的稳定性,而在于a2 是在a1 之前还是之后插入的,答案是在之后(在C++03 中是真的,在C++11 中保证)。如果需要不同的行为,您可以使用插入提示在现有元素之前插入。
    • @JonathanWakely BTW 请查看stackoverflow.com/tags/template-templates/info
    • @knatten:我认为你是对的。这是这种情况的意图,但它仍然没有明确说明。
    【解决方案2】:

    考虑到a1a2 在您的示例中比较相等,而实际存储在std::multiset 中的是a1a2 的副本,我真的不知道如何你知道哪个是哪个。

    如果你能分辨出来,也许class A 一开始就设计得不好。所以std::multiset不保证这样的事情。

    【讨论】:

    • 我认为我的例子不够清楚。假设我有一个类人,它具有各种属性,如年龄、姓名等。现在让我有很多人对象,并想要创建按年龄排序的多组人。我相信这是一个真正的要求。我的问题是,如果这两个人的年龄相同,他们的订单是否保证作为输入订单?
    • 提供给多重集的谓词只要求有部分排序。如果a1 < a2a2 < a1 都评估为假,这并不表示a1 == a2 将评估为真。对象a1a2 是等价的,但不一定是等价的。
    • @Naveen:正如 GMan 在他的回答中解释的那样,这只能从 C++0x 开始得到保证(顺便说一下,我不知道他们已经引入了这个保证;因为这个标准还没有已发布,目前不保证)。
    • @Aeth:是的,你是对的。但通常(强调“通常”)拥有一个所有运算符 和 == 对两个有效对象返回 false 的类并不是一个好主意。迟早,当有人试图以您意想不到的方式使用您的课程时,这将导致意外行为。
    • @Gorpik:我完全同意你所说的比较和相等运算符。但是,OP 没有说明是否已经为他的类型定义了大于和相等运算符。对于他以人和他们的年龄为例,我什至不会使用小于运算符。似乎 operator 方法是专门为与 multiset 一起使用而定义的。我更愿意定义一个不同的方法,例如is_younger,并将其用作多重集的谓词。这样就不会对方法的含义产生混淆。
    【解决方案3】:

    std::multimap 不保证这一点。 如果您可以通过函数使用整数表达您的operator<,例如int A::orderingInt(),你可以使用

    std::multiset<MyCustom> myset;
    

    class MyCustom : public std::vector<A> {}
    

    重载

    bool operator<(const MyCustom& a, const MyCustom& b) {
       // theoretically empty MyCustom should not occure
       return a[0].orderingInt() < b[0].orderingInt();
    }
    

    当然现在添加和迭代会有所不同:

    A a;
    myset[a.orderingInt()].push_back(a);
    
    // groups with "small" elements first
    for(std::multiset<MyCustom>::iterator it=myset.begin(); it!=myset.end(); it++) {
        // those elements are "equal"
        for(std::vector<A>::iterator jt=it->begin(); jt->end(); jt++) { 
             // use A& a = *jt;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2012-10-23
      • 2010-10-06
      • 1970-01-01
      • 2013-03-24
      • 2010-11-05
      • 1970-01-01
      • 2010-10-14
      • 1970-01-01
      相关资源
      最近更新 更多