std::set 在使用 std::set.erase 后包含重复元素

std::set contains duplicate element after using std::set.erase

本文关键字:std set 包含重 元素 erase      更新时间:2023-10-16

嗯,我需要设计一个播放器计时器,该计时器将为每个玩家执行一次 30 分钟。

我没有循环所有玩家,而是做了一个std::set<std::pair<playerid,last_time_executed>>(两者都是整数std::set<std::pair<int,int>>),并且:

  • 我知道如何按时间值排序,
  • 我知道如何在不插入重复玩家 ID 的情况下插入元素(集合中的一个玩家 ID)

但是我不知道如何仅通过 playerid 擦除元素,所以我用我的大脑选择的随机值做了一些测试,结果是:

#include <iostream>
#include <set>
typedef std::pair<int, int> Pair;
struct Cmp{bool operator ()(const Pair &a, const Pair &b){return a.second < b.second;}};
std::set<Pair, Cmp> myset;
int main() {
    myset.insert(Pair(0, 5));
    myset.insert(Pair(1, 0));
    myset.insert(Pair(1, 1));
    myset.erase(Pair(0, 698));
    std::cout << myset.size() << std::endl;
    for (auto i : myset)
        std::cout << "(" << i.first << "," << i.second << ")" << std::endl;
    return 0;
}

这实际上打印了....(请注意重复的 ID "1")

3(1,0)(1,1)(0,5)

虽然这:

int main() {
    myset.insert(Pair(0, 5));
    myset.insert(Pair(1, 0));
    myset.insert(Pair(1, 1));
    std::cout << myset.size() << std::endl;
    for (auto i : myset)
        std::cout << "(" << i.first << "," << i.second << ")" << std::endl;
    return 0;
}

打印这个(没有重复的 ID?

阿拉伯数字(1,1)(0,5)

现在更stangely(!)这个:

int main() {
    myset.insert(Pair(0, 5));
    myset.insert(Pair(1, 0));
    myset.insert(Pair(1, 1));
    myset.erase(Pair(0, 0));
    std::cout << myset.size() << std::endl;
    for (auto i : myset)
        std::cout << "(" << i.first << "," << i.second << ")" << std::endl;
    return 0;
}

打印这个(没有重复的 ID?

阿拉伯数字(1,1)(0,5)

这真的很令人惊讶,因为我期望与第一次测试中的输出相同。

为什么会这样?

您的比较谓词比较该对的second字段。

第一个字段中的"碰撞"无关紧要。

更改它以获得您描述的行为:

struct Cmp {
    bool operator()(const Pair &a, const Pair &b) { return a.first < b.first; }
};

住在科里鲁

此外,正如其他人所注意到的,这更像是std::map<idtype, valuetype>

住在科里鲁

#include <iostream>
#include <map>
std::map<int, int> myset;
int main() {
    mymap.emplace(0, 5);
    mymap.emplace(1, 0);
    mymap.emplace(1, 1);
    mymap.erase(0);
    std::cout << myset.size() << std::endl;
    for (auto i : myset)
        std::cout << "(" << i.first << "," << i.second << ")" << std::endl;
    return 0;
}

请注意,要实际更新(现有)键的值:

    mymap[0] = 5;
    mymap[1] = 0;
    mymap[1] = 1;

std::set使用比较器"确定元素在容器中遵循的顺序以及两个元素键是否等效"。

由于您的比较器仅比较last_time_executed,因此如果它们具有相同的last_time_executed,则认为两个Pair等效。因此,当您执行myset.erase(Pair(0, 0));时,它会擦除Pair(1, 0)

当我运行您的第二个示例时,我得到了重复的玩家 ID,正如我所期望的那样。